singlestoredb 0.4.0__py3-none-any.whl → 1.0.4__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 -1
- singlestoredb/alchemy/__init__.py +90 -0
- singlestoredb/auth.py +5 -1
- singlestoredb/config.py +116 -14
- singlestoredb/connection.py +483 -516
- singlestoredb/converters.py +238 -135
- singlestoredb/exceptions.py +30 -2
- 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/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.py → http/connection.py} +555 -154
- singlestoredb/management/__init__.py +3 -0
- singlestoredb/management/billing_usage.py +148 -0
- singlestoredb/management/cluster.py +14 -6
- singlestoredb/management/manager.py +100 -38
- singlestoredb/management/organization.py +188 -0
- singlestoredb/management/region.py +5 -5
- singlestoredb/management/utils.py +281 -2
- singlestoredb/management/workspace.py +1344 -49
- singlestoredb/{clients/pymysqlsv → mysql}/__init__.py +16 -21
- singlestoredb/{clients/pymysqlsv → mysql}/_auth.py +39 -8
- singlestoredb/{clients/pymysqlsv → mysql}/charset.py +26 -23
- singlestoredb/{clients/pymysqlsv/connections.py → mysql/connection.py} +532 -165
- singlestoredb/{clients/pymysqlsv → mysql}/constants/CLIENT.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/COMMAND.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/CR.py +0 -2
- singlestoredb/{clients/pymysqlsv → mysql}/constants/ER.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/FIELD_TYPE.py +1 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/FLAG.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/SERVER_STATUS.py +0 -1
- singlestoredb/mysql/converters.py +271 -0
- singlestoredb/{clients/pymysqlsv → mysql}/cursors.py +228 -112
- singlestoredb/mysql/err.py +92 -0
- singlestoredb/{clients/pymysqlsv → mysql}/optionfile.py +5 -4
- singlestoredb/{clients/pymysqlsv → mysql}/protocol.py +49 -20
- singlestoredb/mysql/tests/__init__.py +19 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/base.py +32 -12
- singlestoredb/mysql/tests/conftest.py +37 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_DictCursor.py +11 -7
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_SSCursor.py +17 -12
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_basic.py +32 -24
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_connection.py +130 -119
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_converters.py +9 -7
- singlestoredb/mysql/tests/test_cursor.py +141 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_err.py +3 -2
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_issues.py +35 -27
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_load_local.py +13 -11
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_nextset.py +7 -3
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_optionfile.py +2 -1
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/__init__.py +1 -1
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/capabilities.py +19 -17
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/dbapi20.py +31 -22
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +3 -4
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +24 -20
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +4 -4
- singlestoredb/{clients/pymysqlsv → mysql}/times.py +3 -4
- 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 -115
- singlestoredb/tests/test_config.py +13 -13
- singlestoredb/tests/test_connection.py +241 -305
- singlestoredb/tests/test_dbapi.py +27 -0
- 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 -26
- singlestoredb/tests/test_management.py +588 -8
- singlestoredb/tests/test_plugin.py +33 -0
- singlestoredb/tests/test_results.py +11 -12
- singlestoredb/tests/test_udf.py +687 -0
- singlestoredb/tests/utils.py +3 -2
- singlestoredb/utils/config.py +58 -0
- singlestoredb/utils/debug.py +13 -0
- singlestoredb/utils/mogrify.py +151 -0
- singlestoredb/utils/results.py +4 -1
- singlestoredb-1.0.4.dist-info/METADATA +139 -0
- singlestoredb-1.0.4.dist-info/RECORD +112 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/WHEEL +1 -1
- singlestoredb-1.0.4.dist-info/entry_points.txt +2 -0
- singlestoredb/clients/pymysqlsv/converters.py +0 -365
- singlestoredb/clients/pymysqlsv/err.py +0 -144
- singlestoredb/clients/pymysqlsv/tests/__init__.py +0 -19
- singlestoredb/clients/pymysqlsv/tests/test_cursor.py +0 -133
- singlestoredb/clients/pymysqlsv/tests/thirdparty/test_MySQLdb/__init__.py +0 -9
- singlestoredb/drivers/__init__.py +0 -45
- singlestoredb/drivers/base.py +0 -198
- singlestoredb/drivers/cymysql.py +0 -38
- singlestoredb/drivers/http.py +0 -47
- singlestoredb/drivers/mariadb.py +0 -40
- singlestoredb/drivers/mysqlconnector.py +0 -49
- singlestoredb/drivers/mysqldb.py +0 -60
- singlestoredb/drivers/pymysql.py +0 -37
- singlestoredb/drivers/pymysqlsv.py +0 -35
- singlestoredb/drivers/pyodbc.py +0 -65
- singlestoredb-0.4.0.dist-info/METADATA +0 -111
- singlestoredb-0.4.0.dist-info/RECORD +0 -86
- /singlestoredb/{clients → fusion/handlers}/__init__.py +0 -0
- /singlestoredb/{clients/pymysqlsv → mysql}/constants/__init__.py +0 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/LICENSE +0 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/top_level.txt +0 -0
|
@@ -3,9 +3,31 @@
|
|
|
3
3
|
"""Basic SingleStoreDB connection testing."""
|
|
4
4
|
import datetime
|
|
5
5
|
import decimal
|
|
6
|
+
import math
|
|
6
7
|
import os
|
|
7
8
|
import unittest
|
|
8
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
|
+
|
|
9
31
|
import singlestoredb as s2
|
|
10
32
|
from . import utils
|
|
11
33
|
# import traceback
|
|
@@ -29,7 +51,6 @@ class TestBasics(unittest.TestCase):
|
|
|
29
51
|
def setUp(self):
|
|
30
52
|
self.conn = s2.connect(database=type(self).dbname)
|
|
31
53
|
self.cur = self.conn.cursor()
|
|
32
|
-
self.driver = self.conn._driver.dbapi.__name__
|
|
33
54
|
|
|
34
55
|
def tearDown(self):
|
|
35
56
|
try:
|
|
@@ -69,7 +90,7 @@ class TestBasics(unittest.TestCase):
|
|
|
69
90
|
('e', 'elephants', 0),
|
|
70
91
|
]), out
|
|
71
92
|
|
|
72
|
-
assert rowcount
|
|
93
|
+
assert rowcount in (5, -1), rowcount
|
|
73
94
|
assert rownumber == 5, rownumber
|
|
74
95
|
assert lastrowid is None, lastrowid
|
|
75
96
|
assert len(desc) == 3, desc
|
|
@@ -104,7 +125,7 @@ class TestBasics(unittest.TestCase):
|
|
|
104
125
|
('e', 'elephants', 0),
|
|
105
126
|
]), out
|
|
106
127
|
|
|
107
|
-
assert rowcount
|
|
128
|
+
assert rowcount in (5, -1), rowcount
|
|
108
129
|
assert rownumber == 5, rownumber
|
|
109
130
|
assert lastrowid is None, lastrowid
|
|
110
131
|
assert len(desc) == 3, desc
|
|
@@ -140,7 +161,7 @@ class TestBasics(unittest.TestCase):
|
|
|
140
161
|
('e', 'elephants', 0),
|
|
141
162
|
]), out
|
|
142
163
|
|
|
143
|
-
assert rowcount
|
|
164
|
+
assert rowcount in (5, -1), rowcount
|
|
144
165
|
assert rownumber == 5, rownumber
|
|
145
166
|
assert lastrowid is None, lastrowid
|
|
146
167
|
assert len(desc) == 3, desc
|
|
@@ -177,7 +198,7 @@ class TestBasics(unittest.TestCase):
|
|
|
177
198
|
assert self.cur.rownumber == 5, self.cur.rownumber
|
|
178
199
|
|
|
179
200
|
def test_execute_with_dict_params(self):
|
|
180
|
-
self.cur.execute('select * from data where id <
|
|
201
|
+
self.cur.execute('select * from data where id < %(name)s', dict(name='d'))
|
|
181
202
|
out = self.cur.fetchall()
|
|
182
203
|
|
|
183
204
|
desc = self.cur.description
|
|
@@ -190,7 +211,7 @@ class TestBasics(unittest.TestCase):
|
|
|
190
211
|
('c', 'cats', 5),
|
|
191
212
|
]), out
|
|
192
213
|
|
|
193
|
-
assert rowcount
|
|
214
|
+
assert rowcount in (3, -1), rowcount
|
|
194
215
|
assert lastrowid is None, lastrowid
|
|
195
216
|
assert len(desc) == 3, desc
|
|
196
217
|
assert desc[0].name == 'id', desc[0].name
|
|
@@ -200,8 +221,11 @@ class TestBasics(unittest.TestCase):
|
|
|
200
221
|
assert desc[2].name == 'value', desc[2].name
|
|
201
222
|
assert desc[2].type_code == 8, desc[2].type_code
|
|
202
223
|
|
|
224
|
+
with self.assertRaises(KeyError):
|
|
225
|
+
self.cur.execute('select * from data where id < %(name)s', dict(foo='d'))
|
|
226
|
+
|
|
203
227
|
def test_execute_with_positional_params(self):
|
|
204
|
-
self.cur.execute('select * from data where id <
|
|
228
|
+
self.cur.execute('select * from data where id < %s', ['d'])
|
|
205
229
|
out = self.cur.fetchall()
|
|
206
230
|
|
|
207
231
|
desc = self.cur.description
|
|
@@ -214,7 +238,7 @@ class TestBasics(unittest.TestCase):
|
|
|
214
238
|
('c', 'cats', 5),
|
|
215
239
|
]), out
|
|
216
240
|
|
|
217
|
-
assert rowcount
|
|
241
|
+
assert rowcount in (3, -1), rowcount
|
|
218
242
|
assert lastrowid is None, lastrowid
|
|
219
243
|
assert len(desc) == 3, desc
|
|
220
244
|
assert desc[0].name == 'id', desc[0].name
|
|
@@ -224,74 +248,67 @@ class TestBasics(unittest.TestCase):
|
|
|
224
248
|
assert desc[2].name == 'value', desc[2].name
|
|
225
249
|
assert desc[2].type_code == 8, desc[2].type_code
|
|
226
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
|
+
|
|
227
259
|
def test_execute_with_escaped_positional_substitutions(self):
|
|
228
260
|
self.cur.execute(
|
|
229
|
-
'select `id`, `time` from alltypes where `time` =
|
|
261
|
+
'select `id`, `time` from alltypes where `time` = %s', ['00:07:00'],
|
|
230
262
|
)
|
|
231
263
|
out = self.cur.fetchall()
|
|
232
|
-
|
|
233
|
-
assert out[0] == (0, datetime.time(0, 7)), out[0]
|
|
234
|
-
else:
|
|
235
|
-
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
264
|
+
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
236
265
|
|
|
237
266
|
self.cur.execute('select `id`, `time` from alltypes where `time` = "00:07:00"')
|
|
238
267
|
out = self.cur.fetchall()
|
|
239
|
-
|
|
240
|
-
assert out[0] == (0, datetime.time(0, 7)), out[0]
|
|
241
|
-
else:
|
|
242
|
-
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
268
|
+
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
243
269
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
+
# )
|
|
249
275
|
|
|
250
276
|
self.cur.execute(
|
|
251
|
-
'select `id`, `time` from alltypes where `id` =
|
|
252
|
-
'or `time` = "00
|
|
277
|
+
'select `id`, `time` from alltypes where `id` = %s '
|
|
278
|
+
'or `time` = "00:07:00"', [0],
|
|
253
279
|
)
|
|
254
280
|
out = self.cur.fetchall()
|
|
255
|
-
|
|
256
|
-
assert out[0] == (0, datetime.time(0, 7)), out[0]
|
|
257
|
-
else:
|
|
258
|
-
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
281
|
+
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
259
282
|
|
|
260
283
|
def test_execute_with_escaped_substitutions(self):
|
|
261
284
|
self.cur.execute(
|
|
262
|
-
'select `id`, `time` from alltypes where `time` =
|
|
285
|
+
'select `id`, `time` from alltypes where `time` = %(time)s',
|
|
263
286
|
dict(time='00:07:00'),
|
|
264
287
|
)
|
|
265
288
|
out = self.cur.fetchall()
|
|
266
|
-
|
|
267
|
-
assert out[0] == (0, datetime.time(0, 7)), out[0]
|
|
268
|
-
else:
|
|
269
|
-
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
289
|
+
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
270
290
|
|
|
271
291
|
self.cur.execute(
|
|
272
|
-
'select `id`, `time` from alltypes where `time` =
|
|
273
|
-
dict(time='00
|
|
292
|
+
'select `id`, `time` from alltypes where `time` = %(time)s',
|
|
293
|
+
dict(time='00:07:00'),
|
|
274
294
|
)
|
|
275
295
|
out = self.cur.fetchall()
|
|
276
|
-
assert len(out) ==
|
|
296
|
+
assert len(out) == 1, out
|
|
277
297
|
|
|
278
298
|
with self.assertRaises(KeyError):
|
|
279
299
|
self.cur.execute(
|
|
280
300
|
'select `id`, `time`, `char_100` from alltypes '
|
|
281
|
-
'where `time` =
|
|
282
|
-
dict(
|
|
301
|
+
'where `time` = %(time)s or `char_100` like "foo:bar"',
|
|
302
|
+
dict(x='00:07:00'),
|
|
283
303
|
)
|
|
284
304
|
|
|
285
305
|
self.cur.execute(
|
|
286
306
|
'select `id`, `time`, `char_100` from alltypes '
|
|
287
|
-
'where `time` =
|
|
307
|
+
'where `time` = %(time)s or `char_100` like "foo::bar"',
|
|
288
308
|
dict(time='00:07:00'),
|
|
289
309
|
)
|
|
290
310
|
out = self.cur.fetchall()
|
|
291
|
-
|
|
292
|
-
assert out[0][:2] == (0, datetime.time(0, 7)), out[0]
|
|
293
|
-
else:
|
|
294
|
-
assert out[0][:2] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
311
|
+
assert out[0][:2] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
295
312
|
|
|
296
313
|
def test_is_connected(self):
|
|
297
314
|
assert self.conn.is_connected()
|
|
@@ -311,7 +328,7 @@ class TestBasics(unittest.TestCase):
|
|
|
311
328
|
def test_executemany(self):
|
|
312
329
|
# NOTE: Doesn't actually do anything since no rows match
|
|
313
330
|
self.cur.executemany(
|
|
314
|
-
'delete from data where id >
|
|
331
|
+
'delete from data where id > %(name)s',
|
|
315
332
|
[dict(name='z'), dict(name='y')],
|
|
316
333
|
)
|
|
317
334
|
|
|
@@ -482,10 +499,147 @@ class TestBasics(unittest.TestCase):
|
|
|
482
499
|
self.cur.execute('garbage syntax')
|
|
483
500
|
|
|
484
501
|
exc = cm.exception
|
|
485
|
-
|
|
486
|
-
assert exc.errno == 1064, exc.errno
|
|
502
|
+
assert exc.errno == 1064, exc.errno
|
|
487
503
|
assert 'You have an error in your SQL syntax' in exc.errmsg, exc.errmsg
|
|
488
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
|
+
|
|
489
643
|
def test_alltypes(self):
|
|
490
644
|
self.cur.execute('select * from alltypes where id = 0')
|
|
491
645
|
names = [x[0] for x in self.cur.description]
|
|
@@ -496,30 +650,8 @@ class TestBasics(unittest.TestCase):
|
|
|
496
650
|
|
|
497
651
|
bits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
|
498
652
|
|
|
499
|
-
if self.driver == 'pyodbc':
|
|
500
|
-
odbc_types = {
|
|
501
|
-
# int -> bigint
|
|
502
|
-
3: 8, 1: 8, 2: 8, 9: 8,
|
|
503
|
-
# float -> double
|
|
504
|
-
4: 5,
|
|
505
|
-
# timestamp -> datetime
|
|
506
|
-
7: 12,
|
|
507
|
-
# year -> bigint
|
|
508
|
-
13: 8,
|
|
509
|
-
# char/binary -> varchar/varbinary
|
|
510
|
-
249: 15, 250: 15, 251: 15, 252: 15, 253: 15, 254: 15, 255: 15,
|
|
511
|
-
# newdecimal -> decimal
|
|
512
|
-
246: 0,
|
|
513
|
-
# json -> varchar
|
|
514
|
-
245: 15,
|
|
515
|
-
# bit -> varchar
|
|
516
|
-
16: 15,
|
|
517
|
-
}
|
|
518
|
-
else:
|
|
519
|
-
odbc_types = {}
|
|
520
|
-
|
|
521
653
|
def otype(x):
|
|
522
|
-
return
|
|
654
|
+
return x
|
|
523
655
|
|
|
524
656
|
assert row['id'] == 0, row['id']
|
|
525
657
|
assert typ['id'] == otype(3), typ['id']
|
|
@@ -575,21 +707,12 @@ class TestBasics(unittest.TestCase):
|
|
|
575
707
|
assert row['date'] == datetime.date(8524, 11, 10), row['date']
|
|
576
708
|
assert typ['date'] == 10, typ['date']
|
|
577
709
|
|
|
578
|
-
|
|
579
|
-
# put your own converter in, it changes the type code to 15!!!
|
|
580
|
-
if self.driver == 'pyodbc':
|
|
581
|
-
assert row['time'] == datetime.time(0, 7, 0), row['time']
|
|
582
|
-
else:
|
|
583
|
-
assert row['time'] == datetime.timedelta(minutes=7), row['time']
|
|
710
|
+
assert row['time'] == datetime.timedelta(minutes=7), row['time']
|
|
584
711
|
assert typ['time'] == 11, typ['time']
|
|
585
712
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
else:
|
|
590
|
-
assert row['time_6'] == datetime.timedelta(
|
|
591
|
-
hours=1, minutes=10, microseconds=2,
|
|
592
|
-
), row['time_6']
|
|
713
|
+
assert row['time_6'] == datetime.timedelta(
|
|
714
|
+
hours=1, minutes=10, microseconds=2,
|
|
715
|
+
), row['time_6']
|
|
593
716
|
assert typ['time_6'] == 11, typ['time_6']
|
|
594
717
|
|
|
595
718
|
assert row['datetime'] == datetime.datetime(
|
|
@@ -653,11 +776,7 @@ class TestBasics(unittest.TestCase):
|
|
|
653
776
|
assert row['tinyblob'] == bytearray([10, 11, 12, 13, 14, 15]), row['tinyblob']
|
|
654
777
|
assert typ['tinyblob'] == otype(249), typ['tinyblob']
|
|
655
778
|
|
|
656
|
-
|
|
657
|
-
if self.driver == 'pyodbc':
|
|
658
|
-
assert row['json'] == '{"a":10,"b":2.75,"c":"hello world"}', row['json']
|
|
659
|
-
else:
|
|
660
|
-
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']
|
|
661
780
|
assert typ['json'] == otype(245), typ['json']
|
|
662
781
|
|
|
663
782
|
assert row['enum'] == 'one', row['enum']
|
|
@@ -667,11 +786,7 @@ class TestBasics(unittest.TestCase):
|
|
|
667
786
|
assert row['set'] in [{'two'}, 'two'], row['set']
|
|
668
787
|
assert typ['set'] == otype(253), typ['set'] # mysql code: 248
|
|
669
788
|
|
|
670
|
-
|
|
671
|
-
if self.driver == 'pyodbc':
|
|
672
|
-
assert row['bit'] == b'\x80\x00\x00\x00\x00\x00\x00\x00', row['bit']
|
|
673
|
-
else:
|
|
674
|
-
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']
|
|
675
790
|
assert typ['bit'] == otype(16), typ['bit']
|
|
676
791
|
|
|
677
792
|
def test_alltypes_nulls(self):
|
|
@@ -682,30 +797,8 @@ class TestBasics(unittest.TestCase):
|
|
|
682
797
|
row = dict(zip(names, out))
|
|
683
798
|
typ = dict(zip(names, types))
|
|
684
799
|
|
|
685
|
-
if self.driver == 'pyodbc':
|
|
686
|
-
odbc_types = {
|
|
687
|
-
# int -> bigint
|
|
688
|
-
3: 8, 1: 8, 2: 8, 9: 8,
|
|
689
|
-
# float -> double
|
|
690
|
-
4: 5,
|
|
691
|
-
# timestamp -> datetime
|
|
692
|
-
7: 12,
|
|
693
|
-
# year -> bigint
|
|
694
|
-
13: 8,
|
|
695
|
-
# char/binary -> varchar/varbinary
|
|
696
|
-
249: 15, 250: 15, 251: 15, 252: 15, 253: 15, 254: 15, 255: 15,
|
|
697
|
-
# newdecimal -> decimal
|
|
698
|
-
246: 0,
|
|
699
|
-
# json -> varchar
|
|
700
|
-
245: 15,
|
|
701
|
-
# bit -> varchar
|
|
702
|
-
16: 15,
|
|
703
|
-
}
|
|
704
|
-
else:
|
|
705
|
-
odbc_types = {}
|
|
706
|
-
|
|
707
800
|
def otype(x):
|
|
708
|
-
return
|
|
801
|
+
return x
|
|
709
802
|
|
|
710
803
|
assert row['id'] == 1, row['id']
|
|
711
804
|
assert typ['id'] == otype(3), typ['id']
|
|
@@ -830,6 +923,280 @@ class TestBasics(unittest.TestCase):
|
|
|
830
923
|
assert row['bit'] is None, row['bit']
|
|
831
924
|
assert typ['bit'] == otype(16), typ['bit']
|
|
832
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
|
+
|
|
833
1200
|
|
|
834
1201
|
if __name__ == '__main__':
|
|
835
1202
|
import nose2
|