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.

Files changed (120) hide show
  1. singlestoredb/__init__.py +33 -1
  2. singlestoredb/alchemy/__init__.py +90 -0
  3. singlestoredb/auth.py +5 -1
  4. singlestoredb/config.py +116 -14
  5. singlestoredb/connection.py +483 -516
  6. singlestoredb/converters.py +238 -135
  7. singlestoredb/exceptions.py +30 -2
  8. singlestoredb/functions/__init__.py +1 -0
  9. singlestoredb/functions/decorator.py +142 -0
  10. singlestoredb/functions/dtypes.py +1639 -0
  11. singlestoredb/functions/ext/__init__.py +2 -0
  12. singlestoredb/functions/ext/arrow.py +375 -0
  13. singlestoredb/functions/ext/asgi.py +661 -0
  14. singlestoredb/functions/ext/json.py +427 -0
  15. singlestoredb/functions/ext/mmap.py +306 -0
  16. singlestoredb/functions/ext/rowdat_1.py +744 -0
  17. singlestoredb/functions/signature.py +673 -0
  18. singlestoredb/fusion/__init__.py +11 -0
  19. singlestoredb/fusion/graphql.py +213 -0
  20. singlestoredb/fusion/handler.py +621 -0
  21. singlestoredb/fusion/handlers/stage.py +257 -0
  22. singlestoredb/fusion/handlers/utils.py +162 -0
  23. singlestoredb/fusion/handlers/workspace.py +412 -0
  24. singlestoredb/fusion/registry.py +164 -0
  25. singlestoredb/fusion/result.py +399 -0
  26. singlestoredb/http/__init__.py +27 -0
  27. singlestoredb/{http.py → http/connection.py} +555 -154
  28. singlestoredb/management/__init__.py +3 -0
  29. singlestoredb/management/billing_usage.py +148 -0
  30. singlestoredb/management/cluster.py +14 -6
  31. singlestoredb/management/manager.py +100 -38
  32. singlestoredb/management/organization.py +188 -0
  33. singlestoredb/management/region.py +5 -5
  34. singlestoredb/management/utils.py +281 -2
  35. singlestoredb/management/workspace.py +1344 -49
  36. singlestoredb/{clients/pymysqlsv → mysql}/__init__.py +16 -21
  37. singlestoredb/{clients/pymysqlsv → mysql}/_auth.py +39 -8
  38. singlestoredb/{clients/pymysqlsv → mysql}/charset.py +26 -23
  39. singlestoredb/{clients/pymysqlsv/connections.py → mysql/connection.py} +532 -165
  40. singlestoredb/{clients/pymysqlsv → mysql}/constants/CLIENT.py +0 -1
  41. singlestoredb/{clients/pymysqlsv → mysql}/constants/COMMAND.py +0 -1
  42. singlestoredb/{clients/pymysqlsv → mysql}/constants/CR.py +0 -2
  43. singlestoredb/{clients/pymysqlsv → mysql}/constants/ER.py +0 -1
  44. singlestoredb/{clients/pymysqlsv → mysql}/constants/FIELD_TYPE.py +1 -1
  45. singlestoredb/{clients/pymysqlsv → mysql}/constants/FLAG.py +0 -1
  46. singlestoredb/{clients/pymysqlsv → mysql}/constants/SERVER_STATUS.py +0 -1
  47. singlestoredb/mysql/converters.py +271 -0
  48. singlestoredb/{clients/pymysqlsv → mysql}/cursors.py +228 -112
  49. singlestoredb/mysql/err.py +92 -0
  50. singlestoredb/{clients/pymysqlsv → mysql}/optionfile.py +5 -4
  51. singlestoredb/{clients/pymysqlsv → mysql}/protocol.py +49 -20
  52. singlestoredb/mysql/tests/__init__.py +19 -0
  53. singlestoredb/{clients/pymysqlsv → mysql}/tests/base.py +32 -12
  54. singlestoredb/mysql/tests/conftest.py +37 -0
  55. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_DictCursor.py +11 -7
  56. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_SSCursor.py +17 -12
  57. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_basic.py +32 -24
  58. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_connection.py +130 -119
  59. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_converters.py +9 -7
  60. singlestoredb/mysql/tests/test_cursor.py +141 -0
  61. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_err.py +3 -2
  62. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_issues.py +35 -27
  63. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_load_local.py +13 -11
  64. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_nextset.py +7 -3
  65. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_optionfile.py +2 -1
  66. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/__init__.py +1 -1
  67. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  68. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/capabilities.py +19 -17
  69. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/dbapi20.py +31 -22
  70. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +3 -4
  71. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +24 -20
  72. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +4 -4
  73. singlestoredb/{clients/pymysqlsv → mysql}/times.py +3 -4
  74. singlestoredb/pytest.py +283 -0
  75. singlestoredb/tests/empty.sql +0 -0
  76. singlestoredb/tests/ext_funcs/__init__.py +385 -0
  77. singlestoredb/tests/test.sql +210 -0
  78. singlestoredb/tests/test2.sql +1 -0
  79. singlestoredb/tests/test_basics.py +482 -115
  80. singlestoredb/tests/test_config.py +13 -13
  81. singlestoredb/tests/test_connection.py +241 -305
  82. singlestoredb/tests/test_dbapi.py +27 -0
  83. singlestoredb/tests/test_ext_func.py +1193 -0
  84. singlestoredb/tests/test_ext_func_data.py +1101 -0
  85. singlestoredb/tests/test_fusion.py +465 -0
  86. singlestoredb/tests/test_http.py +32 -26
  87. singlestoredb/tests/test_management.py +588 -8
  88. singlestoredb/tests/test_plugin.py +33 -0
  89. singlestoredb/tests/test_results.py +11 -12
  90. singlestoredb/tests/test_udf.py +687 -0
  91. singlestoredb/tests/utils.py +3 -2
  92. singlestoredb/utils/config.py +58 -0
  93. singlestoredb/utils/debug.py +13 -0
  94. singlestoredb/utils/mogrify.py +151 -0
  95. singlestoredb/utils/results.py +4 -1
  96. singlestoredb-1.0.4.dist-info/METADATA +139 -0
  97. singlestoredb-1.0.4.dist-info/RECORD +112 -0
  98. {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/WHEEL +1 -1
  99. singlestoredb-1.0.4.dist-info/entry_points.txt +2 -0
  100. singlestoredb/clients/pymysqlsv/converters.py +0 -365
  101. singlestoredb/clients/pymysqlsv/err.py +0 -144
  102. singlestoredb/clients/pymysqlsv/tests/__init__.py +0 -19
  103. singlestoredb/clients/pymysqlsv/tests/test_cursor.py +0 -133
  104. singlestoredb/clients/pymysqlsv/tests/thirdparty/test_MySQLdb/__init__.py +0 -9
  105. singlestoredb/drivers/__init__.py +0 -45
  106. singlestoredb/drivers/base.py +0 -198
  107. singlestoredb/drivers/cymysql.py +0 -38
  108. singlestoredb/drivers/http.py +0 -47
  109. singlestoredb/drivers/mariadb.py +0 -40
  110. singlestoredb/drivers/mysqlconnector.py +0 -49
  111. singlestoredb/drivers/mysqldb.py +0 -60
  112. singlestoredb/drivers/pymysql.py +0 -37
  113. singlestoredb/drivers/pymysqlsv.py +0 -35
  114. singlestoredb/drivers/pyodbc.py +0 -65
  115. singlestoredb-0.4.0.dist-info/METADATA +0 -111
  116. singlestoredb-0.4.0.dist-info/RECORD +0 -86
  117. /singlestoredb/{clients → fusion/handlers}/__init__.py +0 -0
  118. /singlestoredb/{clients/pymysqlsv → mysql}/constants/__init__.py +0 -0
  119. {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/LICENSE +0 -0
  120. {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.clients.pymysqlsv import err
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.clients.pymysqlsv as sv
10
- from singlestoredb.clients.pymysqlsv import cursors
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
- """undefined methods datetime_or_None, date_or_None"""
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
- """can't retrieve TIMESTAMP fields"""
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
- """query on information_schema.tables fails"""
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
- """exception: TypeError: ord() expected a character, but string of length 0 found"""
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.assertEqual(0, c.execute('SELECT * FROM test'))
90
+ self.assertIn(c.execute('SELECT * FROM test'), [0, -1])
92
91
  c.execute('ALTER TABLE `test` ADD INDEX `idx_station` (`station`)')
93
- self.assertEqual(0, c.execute('SELECT * FROM test'))
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
- """can't handle large result fields"""
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
- """query should be expanded before perform character encoding"""
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 and will not be run.',
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
- """could not connect mysql use passwod"""
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
- "grant all privileges on %s.issue17 to 'issue17user'@'%%' identified by '1234'"
169
- % db,
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(sv.cursors.DictCursor)
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
- """autocommit is not set after reconnecting with ping()"""
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
- 'where value_1 in %s and value_2=%s order by value_1'
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.assertEqual(cur.execute(sql_select, [('a', 'b', 'c'), '\u0430']), 3)
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.clients.pymysqlsv import cursors
5
- from singlestoredb.clients.pymysqlsv import OperationalError
6
- from singlestoredb.clients.pymysqlsv import Warning
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 FIELDS TERMINATED BY ','",
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(cursors.SSCursor)
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 FIELDS TERMINATED BY ','",
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.clients.pymysqlsv as sv
5
- from singlestoredb.clients.pymysqlsv.constants import CLIENT
6
- from singlestoredb.clients.pymysqlsv.tests import base
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.clients.pymysqlsv.optionfile import Parser
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))
@@ -1,4 +1,4 @@
1
- from .test_MySQLdb import *
1
+ from .test_MySQLdb import * # noqa: F403, F401
2
2
 
3
3
  if __name__ == '__main__':
4
4
  import unittest
@@ -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(['%s'] * len(columndefs)),
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
- l = self.cursor.fetchall()
103
+ out = self.cursor.fetchall()
104
104
  if self.debug:
105
- print(l)
106
- self.assertEqual(len(l), self.rows)
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(l[i][j], generator(i, j))
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(['%s'] * len(columndefs)),
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
- l = self.cursor.fetchall()
140
- self.assertEqual(len(l), self.rows)
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(l[i][j], generator(i, j))
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
- l = self.cursor.fetchall()
148
- self.assertFalse(l, "DELETE didn't work")
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
- l = self.cursor.fetchall()
152
- self.assertTrue(len(l) == 1, "ROLLBACK didn't work")
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(['%s'] * len(columndefs)),
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 with execute()',
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 with executemany()',
217
+ 'Over-long columns did not generate warnings/exception '
218
+ 'with executemany()',
217
219
  )
218
220
 
219
221
  self.connection.rollback()