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
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import singlestoredb.mysql as sv
|
|
3
|
+
import singlestoredb.mysql.cursors as cursors
|
|
4
|
+
from singlestoredb.mysql.tests import base
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CursorTest(base.PyMySQLTestCase):
|
|
8
|
+
|
|
9
|
+
def setUp(self):
|
|
10
|
+
super(CursorTest, self).setUp()
|
|
11
|
+
|
|
12
|
+
conn = self.connect()
|
|
13
|
+
self.safe_create_table(
|
|
14
|
+
conn,
|
|
15
|
+
'test',
|
|
16
|
+
'create table test (data varchar(10))',
|
|
17
|
+
)
|
|
18
|
+
cursor = conn.cursor()
|
|
19
|
+
cursor.execute(
|
|
20
|
+
'insert into test (data) values '
|
|
21
|
+
"('row1'), ('row2'), ('row3'), ('row4'), ('row5')",
|
|
22
|
+
)
|
|
23
|
+
cursor.close()
|
|
24
|
+
self.test_connection = sv.connect(**self.databases[0])
|
|
25
|
+
self.addCleanup(self.test_connection.close)
|
|
26
|
+
|
|
27
|
+
def test_cleanup_rows_unbuffered(self):
|
|
28
|
+
with self.connect(cursorclass=cursors.SSCursor) as conn:
|
|
29
|
+
with self.connect(cursorclass=cursors.SSCursor) as conn:
|
|
30
|
+
cursor = conn.cursor()
|
|
31
|
+
|
|
32
|
+
cursor.execute('select * from test as t1, test as t2')
|
|
33
|
+
for counter, row in enumerate(cursor):
|
|
34
|
+
if counter > 10:
|
|
35
|
+
break
|
|
36
|
+
|
|
37
|
+
del cursor
|
|
38
|
+
|
|
39
|
+
c2 = conn.cursor()
|
|
40
|
+
|
|
41
|
+
c2.execute('select 1')
|
|
42
|
+
self.assertEqual(c2.fetchone(), (1,))
|
|
43
|
+
self.assertIsNone(c2.fetchone())
|
|
44
|
+
|
|
45
|
+
def test_cleanup_rows_buffered(self):
|
|
46
|
+
with self.connect(cursorclass=cursors.Cursor) as conn:
|
|
47
|
+
cursor = conn.cursor()
|
|
48
|
+
|
|
49
|
+
cursor.execute('select * from test as t1, test as t2')
|
|
50
|
+
for counter, row in enumerate(cursor):
|
|
51
|
+
if counter > 10:
|
|
52
|
+
break
|
|
53
|
+
|
|
54
|
+
del cursor
|
|
55
|
+
|
|
56
|
+
c2 = conn.cursor()
|
|
57
|
+
c2.execute('select 1')
|
|
58
|
+
|
|
59
|
+
self.assertEqual(c2.fetchone(), (1,))
|
|
60
|
+
self.assertIsNone(c2.fetchone())
|
|
61
|
+
|
|
62
|
+
def test_executemany(self):
|
|
63
|
+
with self.connect(cursorclass=cursors.Cursor) as conn:
|
|
64
|
+
cursor = conn.cursor()
|
|
65
|
+
|
|
66
|
+
m = cursors.RE_INSERT_VALUES.match(
|
|
67
|
+
'INSERT INTO TEST (ID, NAME) VALUES (%s, %s)',
|
|
68
|
+
)
|
|
69
|
+
self.assertIsNotNone(m, 'error parse %s')
|
|
70
|
+
self.assertEqual(
|
|
71
|
+
m.group(3), '', 'group 3 not blank, bug in RE_INSERT_VALUES?',
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
m = cursors.RE_INSERT_VALUES.match(
|
|
75
|
+
'INSERT INTO TEST (ID, NAME) VALUES (%(id)s, %(name)s)',
|
|
76
|
+
)
|
|
77
|
+
self.assertIsNotNone(m, 'error parse %(name)s')
|
|
78
|
+
self.assertEqual(
|
|
79
|
+
m.group(3), '', 'group 3 not blank, bug in RE_INSERT_VALUES?',
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
m = cursors.RE_INSERT_VALUES.match(
|
|
83
|
+
'INSERT INTO TEST (ID, NAME) VALUES (%(id_name)s, %(name)s)',
|
|
84
|
+
)
|
|
85
|
+
self.assertIsNotNone(m, 'error parse %(id_name)s')
|
|
86
|
+
self.assertEqual(
|
|
87
|
+
m.group(3), '', 'group 3 not blank, bug in RE_INSERT_VALUES?',
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
m = cursors.RE_INSERT_VALUES.match(
|
|
91
|
+
'INSERT INTO TEST (ID, NAME) VALUES (%(id_name)s, %(name)s) '
|
|
92
|
+
'ON duplicate update',
|
|
93
|
+
)
|
|
94
|
+
self.assertIsNotNone(m, 'error parse %(id_name)s')
|
|
95
|
+
self.assertEqual(
|
|
96
|
+
m.group(3),
|
|
97
|
+
' ON duplicate update',
|
|
98
|
+
'group 3 not ON duplicate update, bug in RE_INSERT_VALUES?',
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# https://github.com/PyMySQL/PyMySQL/pull/597
|
|
102
|
+
m = cursors.RE_INSERT_VALUES.match(
|
|
103
|
+
'INSERT INTO bloup(foo, bar)VALUES(%s, %s)',
|
|
104
|
+
)
|
|
105
|
+
assert m is not None
|
|
106
|
+
|
|
107
|
+
# cursor._executed must be "insert into test (data)
|
|
108
|
+
# values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)"
|
|
109
|
+
# list args
|
|
110
|
+
data = range(10)
|
|
111
|
+
cursor.executemany('insert into test (data) values (%s)', data)
|
|
112
|
+
self.assertTrue(
|
|
113
|
+
cursor._executed.endswith(b',(7),(8),(9)'),
|
|
114
|
+
'execute many with %s not in one query',
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# dict args
|
|
118
|
+
data_dict = [{'data': i} for i in range(10)]
|
|
119
|
+
cursor.executemany('insert into test (data) values (%(data)s)', data_dict)
|
|
120
|
+
self.assertTrue(
|
|
121
|
+
cursor._executed.endswith(b',(7),(8),(9)'),
|
|
122
|
+
'execute many with %(data)s not in one query',
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# %% in column set
|
|
126
|
+
cursor.execute(
|
|
127
|
+
"""\
|
|
128
|
+
CREATE TABLE percent_test (
|
|
129
|
+
`A%` INTEGER,
|
|
130
|
+
`B%` INTEGER)""",
|
|
131
|
+
)
|
|
132
|
+
try:
|
|
133
|
+
q = 'INSERT INTO percent_test (`A%%`, `B%%`) VALUES (%s, %s)'
|
|
134
|
+
self.assertIsNotNone(cursors.RE_INSERT_VALUES.match(q))
|
|
135
|
+
cursor.executemany(q, [(3, 4), (5, 6)])
|
|
136
|
+
self.assertTrue(
|
|
137
|
+
cursor._executed.endswith(b'(3, 4),(5, 6)'),
|
|
138
|
+
'executemany with %% not in one query',
|
|
139
|
+
)
|
|
140
|
+
finally:
|
|
141
|
+
cursor.execute('DROP TABLE IF EXISTS percent_test')
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# type: ignore
|
|
2
2
|
import unittest
|
|
3
3
|
|
|
4
|
-
from singlestoredb.
|
|
4
|
+
from singlestoredb.mysql import err
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
__all__ = ['TestRaiseException']
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class TestRaiseException(unittest.TestCase):
|
|
11
|
+
|
|
11
12
|
def test_raise_mysql_exception(self):
|
|
12
13
|
data = b'\xff\x15\x04#28000Access denied'
|
|
13
14
|
with self.assertRaises(err.OperationalError) as cm:
|
|
14
15
|
err.raise_mysql_exception(data)
|
|
15
|
-
self.assertEqual(cm.exception.args, (1045, 'Access denied'))
|
|
16
|
+
self.assertEqual(cm.exception.args, (1045, 'Access denied', None))
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
# type: ignore
|
|
2
2
|
import datetime
|
|
3
|
-
import sys
|
|
4
3
|
import time
|
|
5
4
|
import warnings
|
|
6
5
|
|
|
7
6
|
import pytest
|
|
8
7
|
|
|
9
|
-
import singlestoredb.
|
|
10
|
-
from singlestoredb.
|
|
11
|
-
from singlestoredb.clients.pymysqlsv.tests import base
|
|
8
|
+
import singlestoredb.mysql as sv
|
|
9
|
+
from singlestoredb.mysql.tests import base
|
|
12
10
|
|
|
13
11
|
__all__ = ['TestOldIssues', 'TestNewIssues', 'TestGitHubIssues']
|
|
14
12
|
|
|
15
13
|
|
|
16
14
|
class TestOldIssues(base.PyMySQLTestCase):
|
|
15
|
+
|
|
17
16
|
def test_issue_3(self):
|
|
18
|
-
"""
|
|
17
|
+
"""Undefined methods datetime_or_None, date_or_None."""
|
|
19
18
|
conn = self.connect()
|
|
20
19
|
c = conn.cursor()
|
|
21
20
|
with warnings.catch_warnings():
|
|
@@ -43,7 +42,7 @@ class TestOldIssues(base.PyMySQLTestCase):
|
|
|
43
42
|
c.execute('drop table issue3')
|
|
44
43
|
|
|
45
44
|
def test_issue_4(self):
|
|
46
|
-
"""
|
|
45
|
+
"""Can't retrieve TIMESTAMP fields."""
|
|
47
46
|
conn = self.connect()
|
|
48
47
|
c = conn.cursor()
|
|
49
48
|
with warnings.catch_warnings():
|
|
@@ -58,14 +57,14 @@ class TestOldIssues(base.PyMySQLTestCase):
|
|
|
58
57
|
c.execute('drop table issue4')
|
|
59
58
|
|
|
60
59
|
def test_issue_5(self):
|
|
61
|
-
"""
|
|
60
|
+
"""Query on information_schema.tables fails."""
|
|
62
61
|
con = self.connect()
|
|
63
62
|
cur = con.cursor()
|
|
64
63
|
cur.execute('select * from information_schema.tables')
|
|
65
64
|
|
|
66
65
|
@pytest.mark.skip(reason='database is not created')
|
|
67
66
|
def test_issue_6(self):
|
|
68
|
-
"""
|
|
67
|
+
"""TypeError: ord() expected a character, but string of length 0 found."""
|
|
69
68
|
# ToDo: this test requires access to db 'mysql'.
|
|
70
69
|
kwargs = self.databases[0].copy()
|
|
71
70
|
kwargs['database'] = 'mysql'
|
|
@@ -75,7 +74,7 @@ class TestOldIssues(base.PyMySQLTestCase):
|
|
|
75
74
|
conn.close()
|
|
76
75
|
|
|
77
76
|
def test_issue_8(self):
|
|
78
|
-
"""Primary Key and Index error when selecting data"""
|
|
77
|
+
"""Primary Key and Index error when selecting data."""
|
|
79
78
|
conn = self.connect()
|
|
80
79
|
c = conn.cursor()
|
|
81
80
|
with warnings.catch_warnings():
|
|
@@ -88,14 +87,14 @@ DEFAULT '0', `me` double DEFAULT NULL, `mo` double DEFAULT NULL, PRIMARY
|
|
|
88
87
|
KEY (`station`,`dh`,`echeance`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;""",
|
|
89
88
|
)
|
|
90
89
|
try:
|
|
91
|
-
self.
|
|
90
|
+
self.assertIn(c.execute('SELECT * FROM test'), [0, -1])
|
|
92
91
|
c.execute('ALTER TABLE `test` ADD INDEX `idx_station` (`station`)')
|
|
93
|
-
self.
|
|
92
|
+
self.assertIn(c.execute('SELECT * FROM test'), [0, -1])
|
|
94
93
|
finally:
|
|
95
94
|
c.execute('drop table test')
|
|
96
95
|
|
|
97
96
|
def test_issue_13(self):
|
|
98
|
-
"""
|
|
97
|
+
"""Can't handle large result fields."""
|
|
99
98
|
conn = self.connect()
|
|
100
99
|
cur = conn.cursor()
|
|
101
100
|
with warnings.catch_warnings():
|
|
@@ -114,7 +113,7 @@ KEY (`station`,`dh`,`echeance`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;""",
|
|
|
114
113
|
cur.execute('drop table issue13')
|
|
115
114
|
|
|
116
115
|
def test_issue_15(self):
|
|
117
|
-
"""
|
|
116
|
+
"""Query should be expanded before perform character encoding."""
|
|
118
117
|
conn = self.connect()
|
|
119
118
|
c = conn.cursor()
|
|
120
119
|
with warnings.catch_warnings():
|
|
@@ -129,7 +128,7 @@ KEY (`station`,`dh`,`echeance`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;""",
|
|
|
129
128
|
c.execute('drop table issue15')
|
|
130
129
|
|
|
131
130
|
def test_issue_16(self):
|
|
132
|
-
"""Patch for string and tuple escaping"""
|
|
131
|
+
"""Patch for string and tuple escaping."""
|
|
133
132
|
conn = self.connect()
|
|
134
133
|
c = conn.cursor()
|
|
135
134
|
with warnings.catch_warnings():
|
|
@@ -148,10 +147,11 @@ KEY (`station`,`dh`,`echeance`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;""",
|
|
|
148
147
|
c.execute('drop table issue16')
|
|
149
148
|
|
|
150
149
|
@pytest.mark.skip(
|
|
151
|
-
'test_issue_17() requires a custom, legacy MySQL configuration
|
|
150
|
+
'test_issue_17() requires a custom, legacy MySQL configuration '
|
|
151
|
+
'and will not be run.',
|
|
152
152
|
)
|
|
153
153
|
def test_issue_17(self):
|
|
154
|
-
"""
|
|
154
|
+
"""Could not connect mysql use password."""
|
|
155
155
|
conn = self.connect()
|
|
156
156
|
host = self.databases[0]['host']
|
|
157
157
|
db = self.databases[0]['database']
|
|
@@ -165,8 +165,10 @@ KEY (`station`,`dh`,`echeance`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;""",
|
|
|
165
165
|
c.execute('create table issue17 (x varchar(32) primary key)')
|
|
166
166
|
c.execute("insert into issue17 (x) values ('hello, world!')")
|
|
167
167
|
c.execute(
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
(
|
|
169
|
+
"grant all privileges on %s.issue17 to 'issue17user'@'%%' "
|
|
170
|
+
"identified by '1234'"
|
|
171
|
+
) % db,
|
|
170
172
|
)
|
|
171
173
|
conn.commit()
|
|
172
174
|
|
|
@@ -179,6 +181,7 @@ KEY (`station`,`dh`,`echeance`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;""",
|
|
|
179
181
|
|
|
180
182
|
|
|
181
183
|
class TestNewIssues(base.PyMySQLTestCase):
|
|
184
|
+
|
|
182
185
|
def test_issue_34(self):
|
|
183
186
|
try:
|
|
184
187
|
sv.connect(host='localhost', port=1237, user='root')
|
|
@@ -287,8 +290,9 @@ class TestNewIssues(base.PyMySQLTestCase):
|
|
|
287
290
|
|
|
288
291
|
|
|
289
292
|
class TestGitHubIssues(base.PyMySQLTestCase):
|
|
293
|
+
|
|
290
294
|
def test_issue_66(self):
|
|
291
|
-
"""'Connection' object has no attribute 'insert_id'"""
|
|
295
|
+
"""'Connection' object has no attribute 'insert_id'."""
|
|
292
296
|
conn = self.connect()
|
|
293
297
|
c = conn.cursor()
|
|
294
298
|
self.assertEqual(0, conn.insert_id())
|
|
@@ -306,9 +310,9 @@ class TestGitHubIssues(base.PyMySQLTestCase):
|
|
|
306
310
|
c.execute('drop table issue66')
|
|
307
311
|
|
|
308
312
|
def test_issue_79(self):
|
|
309
|
-
"""Duplicate field overwrites the previous one in the result of DictCursor"""
|
|
310
|
-
conn = self.connect()
|
|
311
|
-
c = conn.cursor(
|
|
313
|
+
"""Duplicate field overwrites the previous one in the result of DictCursor."""
|
|
314
|
+
conn = self.connect(cursorclass=sv.cursors.DictCursor)
|
|
315
|
+
c = conn.cursor()
|
|
312
316
|
|
|
313
317
|
with warnings.catch_warnings():
|
|
314
318
|
warnings.filterwarnings('ignore')
|
|
@@ -333,7 +337,7 @@ class TestGitHubIssues(base.PyMySQLTestCase):
|
|
|
333
337
|
c.execute('drop table b')
|
|
334
338
|
|
|
335
339
|
def test_issue_95(self):
|
|
336
|
-
"""Leftover trailing OK packet for "CALL my_sp" queries"""
|
|
340
|
+
"""Leftover trailing OK packet for "CALL my_sp" queries."""
|
|
337
341
|
conn = self.connect()
|
|
338
342
|
cur = conn.cursor()
|
|
339
343
|
with warnings.catch_warnings():
|
|
@@ -355,10 +359,14 @@ class TestGitHubIssues(base.PyMySQLTestCase):
|
|
|
355
359
|
cur.execute('DROP PROCEDURE IF EXISTS `foo`')
|
|
356
360
|
|
|
357
361
|
def test_issue_114(self):
|
|
358
|
-
"""
|
|
362
|
+
"""Autocommit is not set after reconnecting with ping()."""
|
|
359
363
|
conn = sv.connect(charset='utf8', **self.databases[0])
|
|
360
364
|
conn.autocommit(False)
|
|
361
365
|
c = conn.cursor()
|
|
366
|
+
|
|
367
|
+
if type(c).__name__.startswith('SS'):
|
|
368
|
+
self.skipTest('Test hangs with unbuffered cursor')
|
|
369
|
+
|
|
362
370
|
c.execute("""select @@autocommit;""")
|
|
363
371
|
self.assertFalse(c.fetchone()[0])
|
|
364
372
|
conn.close()
|
|
@@ -380,7 +388,7 @@ class TestGitHubIssues(base.PyMySQLTestCase):
|
|
|
380
388
|
conn.close()
|
|
381
389
|
|
|
382
390
|
def test_issue_175(self):
|
|
383
|
-
"""The number of fields returned by server is read in wrong way"""
|
|
391
|
+
"""The number of fields returned by server is read in wrong way."""
|
|
384
392
|
conn = self.connect()
|
|
385
393
|
cur = conn.cursor()
|
|
386
394
|
for length in (200, 300):
|
|
@@ -410,7 +418,7 @@ class TestGitHubIssues(base.PyMySQLTestCase):
|
|
|
410
418
|
'values (%(value_1)s, %(value_2)s)'
|
|
411
419
|
)
|
|
412
420
|
sql_select = 'select * from issue321 ' + \
|
|
413
|
-
|
|
421
|
+
'where value_1 in %s and value_2=%s order by value_1'
|
|
414
422
|
data = [
|
|
415
423
|
[('a',), '\u0430'],
|
|
416
424
|
[['b'], '\u0430'],
|
|
@@ -420,7 +428,7 @@ class TestGitHubIssues(base.PyMySQLTestCase):
|
|
|
420
428
|
self.assertEqual(cur.execute(sql_insert, data[0]), 1)
|
|
421
429
|
self.assertEqual(cur.execute(sql_insert, data[1]), 1)
|
|
422
430
|
self.assertEqual(cur.execute(sql_dict_insert, data[2]), 1)
|
|
423
|
-
self.
|
|
431
|
+
self.assertIn(cur.execute(sql_select, [('a', 'b', 'c'), '\u0430']), [3, -1])
|
|
424
432
|
self.assertEqual(cur.fetchone(), ('a', '\u0430'))
|
|
425
433
|
self.assertEqual(cur.fetchone(), ('b', '\u0430'))
|
|
426
434
|
self.assertEqual(cur.fetchone(), ('c', '\u0430'))
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# type: ignore
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
-
from singlestoredb.
|
|
5
|
-
from singlestoredb.
|
|
6
|
-
from singlestoredb.
|
|
7
|
-
from singlestoredb.clients.pymysqlsv.tests import base
|
|
4
|
+
from singlestoredb.mysql import cursors
|
|
5
|
+
from singlestoredb.mysql import OperationalError
|
|
6
|
+
from singlestoredb.mysql.tests import base
|
|
8
7
|
|
|
9
8
|
__all__ = ['TestLoadLocal']
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class TestLoadLocal(base.PyMySQLTestCase):
|
|
12
|
+
|
|
13
13
|
def test_no_file(self):
|
|
14
|
-
"""Test load local infile when the file does not exist"""
|
|
14
|
+
"""Test load local infile when the file does not exist."""
|
|
15
15
|
conn = self.connect()
|
|
16
16
|
c = conn.cursor()
|
|
17
17
|
c.execute('CREATE TABLE test_load_local (a INTEGER, b INTEGER, c TEXT)')
|
|
@@ -29,7 +29,7 @@ class TestLoadLocal(base.PyMySQLTestCase):
|
|
|
29
29
|
c.close()
|
|
30
30
|
|
|
31
31
|
def test_load_file(self):
|
|
32
|
-
"""Test load local infile with a valid file"""
|
|
32
|
+
"""Test load local infile with a valid file."""
|
|
33
33
|
conn = self.connect()
|
|
34
34
|
c = conn.cursor()
|
|
35
35
|
c.execute('CREATE TABLE test_load_local (a INTEGER, b INTEGER, c TEXT)')
|
|
@@ -38,7 +38,8 @@ class TestLoadLocal(base.PyMySQLTestCase):
|
|
|
38
38
|
)
|
|
39
39
|
try:
|
|
40
40
|
c.execute(
|
|
41
|
-
f"LOAD DATA LOCAL INFILE '{filename}' INTO TABLE test_load_local
|
|
41
|
+
f"LOAD DATA LOCAL INFILE '{filename}' INTO TABLE test_load_local "
|
|
42
|
+
"FIELDS TERMINATED BY ','",
|
|
42
43
|
)
|
|
43
44
|
c.execute('SELECT COUNT(*) FROM test_load_local')
|
|
44
45
|
self.assertEqual(22749, c.fetchone()[0])
|
|
@@ -46,16 +47,17 @@ class TestLoadLocal(base.PyMySQLTestCase):
|
|
|
46
47
|
c.execute('DROP TABLE test_load_local')
|
|
47
48
|
|
|
48
49
|
def test_unbuffered_load_file(self):
|
|
49
|
-
"""Test unbuffered load local infile with a valid file"""
|
|
50
|
-
conn = self.connect()
|
|
51
|
-
c = conn.cursor(
|
|
50
|
+
"""Test unbuffered load local infile with a valid file."""
|
|
51
|
+
conn = self.connect(cursorclass=cursors.SSCursor)
|
|
52
|
+
c = conn.cursor()
|
|
52
53
|
c.execute('CREATE TABLE test_load_local (a INTEGER, b INTEGER, c TEXT)')
|
|
53
54
|
filename = os.path.join(
|
|
54
55
|
os.path.dirname(os.path.realpath(__file__)), 'data', 'load_local_data.txt',
|
|
55
56
|
)
|
|
56
57
|
try:
|
|
57
58
|
c.execute(
|
|
58
|
-
f"LOAD DATA LOCAL INFILE '{filename}' INTO TABLE test_load_local
|
|
59
|
+
f"LOAD DATA LOCAL INFILE '{filename}' INTO TABLE test_load_local "
|
|
60
|
+
"FIELDS TERMINATED BY ','",
|
|
59
61
|
)
|
|
60
62
|
c.execute('SELECT COUNT(*) FROM test_load_local')
|
|
61
63
|
self.assertEqual(22749, c.fetchone()[0])
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# type: ignore
|
|
2
2
|
import pytest
|
|
3
3
|
|
|
4
|
-
import singlestoredb.
|
|
5
|
-
from singlestoredb.
|
|
6
|
-
from singlestoredb.
|
|
4
|
+
import singlestoredb.mysql as sv
|
|
5
|
+
from singlestoredb.mysql.constants import CLIENT
|
|
6
|
+
from singlestoredb.mysql.tests import base
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TestNextset(base.PyMySQLTestCase):
|
|
10
|
+
|
|
10
11
|
def test_nextset(self):
|
|
11
12
|
con = self.connect(
|
|
12
13
|
init_command='SELECT "bar"; SELECT "baz"',
|
|
@@ -34,6 +35,9 @@ class TestNextset(base.PyMySQLTestCase):
|
|
|
34
35
|
con = self.connect(client_flag=CLIENT.MULTI_STATEMENTS)
|
|
35
36
|
cur = con.cursor()
|
|
36
37
|
|
|
38
|
+
if type(cur).__name__.startswith('SS'):
|
|
39
|
+
self.skipTest('nextset in unbuffered cursor closes connection')
|
|
40
|
+
|
|
37
41
|
for i in range(3):
|
|
38
42
|
cur.execute('SELECT %s; xyzzy;', (i,))
|
|
39
43
|
self.assertEqual([(i,)], list(cur))
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from io import StringIO
|
|
3
3
|
from unittest import TestCase
|
|
4
4
|
|
|
5
|
-
from singlestoredb.
|
|
5
|
+
from singlestoredb.mysql.optionfile import Parser
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
__all__ = ['TestParser']
|
|
@@ -18,6 +18,7 @@ skip-slave-start
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class TestParser(TestCase):
|
|
21
|
+
|
|
21
22
|
def test_string(self):
|
|
22
23
|
parser = Parser()
|
|
23
24
|
parser.read_file(StringIO(_cfg_file))
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
from .test_MySQLdb_capabilities import test_MySQLdb as test_capabilities # noqa: F401
|
|
3
|
+
from .test_MySQLdb_dbapi20 import test_MySQLdb as test_dbapi2 # noqa: F401
|
|
4
|
+
from .test_MySQLdb_nonstandard import * # noqa: F401, F403
|
|
5
|
+
|
|
6
|
+
if __name__ == '__main__':
|
|
7
|
+
import unittest
|
|
8
|
+
|
|
9
|
+
unittest.main()
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
Adapted from a script by M-A Lemburg.
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
-
import sys
|
|
9
8
|
import unittest
|
|
10
9
|
from time import time
|
|
11
10
|
|
|
@@ -74,6 +73,7 @@ class DatabaseTest(unittest.TestCase):
|
|
|
74
73
|
generator must be a function taking arguments (row_number,
|
|
75
74
|
col_number) returning a suitable data object for insertion
|
|
76
75
|
into the table.
|
|
76
|
+
|
|
77
77
|
"""
|
|
78
78
|
self.table = self.new_table_name()
|
|
79
79
|
self.cursor.execute(
|
|
@@ -86,7 +86,7 @@ class DatabaseTest(unittest.TestCase):
|
|
|
86
86
|
self.create_table(columndefs)
|
|
87
87
|
insert_statement = 'INSERT INTO %s VALUES (%s)' % (
|
|
88
88
|
self.table,
|
|
89
|
-
','.join(
|
|
89
|
+
','.join('%s' for x in range(len(columndefs))),
|
|
90
90
|
)
|
|
91
91
|
data = [
|
|
92
92
|
[generator(i, j) for j in range(len(columndefs))] for i in range(self.rows)
|
|
@@ -100,14 +100,14 @@ class DatabaseTest(unittest.TestCase):
|
|
|
100
100
|
'select * from %s ORDER BY %s' %
|
|
101
101
|
(self.table, columndefs[0].split()[0]),
|
|
102
102
|
)
|
|
103
|
-
|
|
103
|
+
out = self.cursor.fetchall()
|
|
104
104
|
if self.debug:
|
|
105
|
-
print(
|
|
106
|
-
self.assertEqual(len(
|
|
105
|
+
print(out)
|
|
106
|
+
self.assertEqual(len(out), self.rows)
|
|
107
107
|
try:
|
|
108
108
|
for i in range(self.rows):
|
|
109
109
|
for j in range(len(columndefs)):
|
|
110
|
-
self.assertEqual(
|
|
110
|
+
self.assertEqual(out[i][j], generator(i, j))
|
|
111
111
|
finally:
|
|
112
112
|
if not self.debug:
|
|
113
113
|
self.cursor.execute('drop table %s' % (self.table))
|
|
@@ -124,7 +124,7 @@ class DatabaseTest(unittest.TestCase):
|
|
|
124
124
|
self.create_table(columndefs)
|
|
125
125
|
insert_statement = 'INSERT INTO %s VALUES (%s)' % (
|
|
126
126
|
self.table,
|
|
127
|
-
','.join(
|
|
127
|
+
','.join('%s' for x in range(len(columndefs))),
|
|
128
128
|
)
|
|
129
129
|
data = [
|
|
130
130
|
[generator(i, j) for j in range(len(columndefs))] for i in range(self.rows)
|
|
@@ -136,20 +136,20 @@ class DatabaseTest(unittest.TestCase):
|
|
|
136
136
|
'select * from %s ORDER BY %s' %
|
|
137
137
|
(self.table, columndefs[0].split()[0]),
|
|
138
138
|
)
|
|
139
|
-
|
|
140
|
-
self.assertEqual(len(
|
|
139
|
+
out = self.cursor.fetchall()
|
|
140
|
+
self.assertEqual(len(out), self.rows)
|
|
141
141
|
for i in range(self.rows):
|
|
142
142
|
for j in range(len(columndefs)):
|
|
143
|
-
self.assertEqual(
|
|
143
|
+
self.assertEqual(out[i][j], generator(i, j))
|
|
144
144
|
delete_statement = 'delete from %s where col1=%%s' % self.table
|
|
145
145
|
self.cursor.execute(delete_statement, (0,))
|
|
146
146
|
self.cursor.execute('select col1 from %s where col1=%s' % (self.table, 0))
|
|
147
|
-
|
|
148
|
-
self.assertFalse(
|
|
147
|
+
out = self.cursor.fetchall()
|
|
148
|
+
self.assertFalse(out, "DELETE didn't work")
|
|
149
149
|
self.connection.rollback()
|
|
150
150
|
self.cursor.execute('select col1 from %s where col1=%s' % (self.table, 0))
|
|
151
|
-
|
|
152
|
-
self.assertTrue(len(
|
|
151
|
+
out = self.cursor.fetchall()
|
|
152
|
+
self.assertTrue(len(out) == 1, "ROLLBACK didn't work")
|
|
153
153
|
self.cursor.execute('drop table %s' % (self.table))
|
|
154
154
|
|
|
155
155
|
@pytest.mark.skip(reason='SingleStoreDB does not surface a warning')
|
|
@@ -165,7 +165,7 @@ class DatabaseTest(unittest.TestCase):
|
|
|
165
165
|
self.create_table(columndefs)
|
|
166
166
|
insert_statement = 'INSERT INTO %s VALUES (%s)' % (
|
|
167
167
|
self.table,
|
|
168
|
-
','.join(
|
|
168
|
+
','.join('%s' for x in range(len(columndefs))),
|
|
169
169
|
)
|
|
170
170
|
|
|
171
171
|
try:
|
|
@@ -195,7 +195,8 @@ class DatabaseTest(unittest.TestCase):
|
|
|
195
195
|
pass
|
|
196
196
|
else:
|
|
197
197
|
self.fail(
|
|
198
|
-
'Over-long columns did not generate warnings/exception
|
|
198
|
+
'Over-long columns did not generate warnings/exception '
|
|
199
|
+
'with execute()',
|
|
199
200
|
)
|
|
200
201
|
|
|
201
202
|
self.connection.rollback()
|
|
@@ -213,7 +214,8 @@ class DatabaseTest(unittest.TestCase):
|
|
|
213
214
|
pass
|
|
214
215
|
else:
|
|
215
216
|
self.fail(
|
|
216
|
-
'Over-long columns did not generate warnings/exception
|
|
217
|
+
'Over-long columns did not generate warnings/exception '
|
|
218
|
+
'with executemany()',
|
|
217
219
|
)
|
|
218
220
|
|
|
219
221
|
self.connection.rollback()
|