singlestoredb 1.16.1__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.
Files changed (183) hide show
  1. singlestoredb/__init__.py +75 -0
  2. singlestoredb/ai/__init__.py +2 -0
  3. singlestoredb/ai/chat.py +139 -0
  4. singlestoredb/ai/embeddings.py +128 -0
  5. singlestoredb/alchemy/__init__.py +90 -0
  6. singlestoredb/apps/__init__.py +3 -0
  7. singlestoredb/apps/_cloud_functions.py +90 -0
  8. singlestoredb/apps/_config.py +72 -0
  9. singlestoredb/apps/_connection_info.py +18 -0
  10. singlestoredb/apps/_dashboards.py +47 -0
  11. singlestoredb/apps/_process.py +32 -0
  12. singlestoredb/apps/_python_udfs.py +100 -0
  13. singlestoredb/apps/_stdout_supress.py +30 -0
  14. singlestoredb/apps/_uvicorn_util.py +36 -0
  15. singlestoredb/auth.py +245 -0
  16. singlestoredb/config.py +484 -0
  17. singlestoredb/connection.py +1487 -0
  18. singlestoredb/converters.py +950 -0
  19. singlestoredb/docstring/__init__.py +33 -0
  20. singlestoredb/docstring/attrdoc.py +126 -0
  21. singlestoredb/docstring/common.py +230 -0
  22. singlestoredb/docstring/epydoc.py +267 -0
  23. singlestoredb/docstring/google.py +412 -0
  24. singlestoredb/docstring/numpydoc.py +562 -0
  25. singlestoredb/docstring/parser.py +100 -0
  26. singlestoredb/docstring/py.typed +1 -0
  27. singlestoredb/docstring/rest.py +256 -0
  28. singlestoredb/docstring/tests/__init__.py +1 -0
  29. singlestoredb/docstring/tests/_pydoctor.py +21 -0
  30. singlestoredb/docstring/tests/test_epydoc.py +729 -0
  31. singlestoredb/docstring/tests/test_google.py +1007 -0
  32. singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
  33. singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
  34. singlestoredb/docstring/tests/test_parser.py +248 -0
  35. singlestoredb/docstring/tests/test_rest.py +547 -0
  36. singlestoredb/docstring/tests/test_util.py +70 -0
  37. singlestoredb/docstring/util.py +141 -0
  38. singlestoredb/exceptions.py +120 -0
  39. singlestoredb/functions/__init__.py +16 -0
  40. singlestoredb/functions/decorator.py +201 -0
  41. singlestoredb/functions/dtypes.py +1793 -0
  42. singlestoredb/functions/ext/__init__.py +1 -0
  43. singlestoredb/functions/ext/arrow.py +375 -0
  44. singlestoredb/functions/ext/asgi.py +2133 -0
  45. singlestoredb/functions/ext/json.py +420 -0
  46. singlestoredb/functions/ext/mmap.py +413 -0
  47. singlestoredb/functions/ext/rowdat_1.py +724 -0
  48. singlestoredb/functions/ext/timer.py +89 -0
  49. singlestoredb/functions/ext/utils.py +218 -0
  50. singlestoredb/functions/signature.py +1578 -0
  51. singlestoredb/functions/typing/__init__.py +41 -0
  52. singlestoredb/functions/typing/numpy.py +20 -0
  53. singlestoredb/functions/typing/pandas.py +2 -0
  54. singlestoredb/functions/typing/polars.py +2 -0
  55. singlestoredb/functions/typing/pyarrow.py +2 -0
  56. singlestoredb/functions/utils.py +421 -0
  57. singlestoredb/fusion/__init__.py +11 -0
  58. singlestoredb/fusion/graphql.py +213 -0
  59. singlestoredb/fusion/handler.py +916 -0
  60. singlestoredb/fusion/handlers/__init__.py +0 -0
  61. singlestoredb/fusion/handlers/export.py +525 -0
  62. singlestoredb/fusion/handlers/files.py +690 -0
  63. singlestoredb/fusion/handlers/job.py +660 -0
  64. singlestoredb/fusion/handlers/models.py +250 -0
  65. singlestoredb/fusion/handlers/stage.py +502 -0
  66. singlestoredb/fusion/handlers/utils.py +324 -0
  67. singlestoredb/fusion/handlers/workspace.py +956 -0
  68. singlestoredb/fusion/registry.py +249 -0
  69. singlestoredb/fusion/result.py +399 -0
  70. singlestoredb/http/__init__.py +27 -0
  71. singlestoredb/http/connection.py +1267 -0
  72. singlestoredb/magics/__init__.py +34 -0
  73. singlestoredb/magics/run_personal.py +137 -0
  74. singlestoredb/magics/run_shared.py +134 -0
  75. singlestoredb/management/__init__.py +9 -0
  76. singlestoredb/management/billing_usage.py +148 -0
  77. singlestoredb/management/cluster.py +462 -0
  78. singlestoredb/management/export.py +295 -0
  79. singlestoredb/management/files.py +1102 -0
  80. singlestoredb/management/inference_api.py +105 -0
  81. singlestoredb/management/job.py +887 -0
  82. singlestoredb/management/manager.py +373 -0
  83. singlestoredb/management/organization.py +226 -0
  84. singlestoredb/management/region.py +169 -0
  85. singlestoredb/management/utils.py +423 -0
  86. singlestoredb/management/workspace.py +1927 -0
  87. singlestoredb/mysql/__init__.py +177 -0
  88. singlestoredb/mysql/_auth.py +298 -0
  89. singlestoredb/mysql/charset.py +214 -0
  90. singlestoredb/mysql/connection.py +2032 -0
  91. singlestoredb/mysql/constants/CLIENT.py +38 -0
  92. singlestoredb/mysql/constants/COMMAND.py +32 -0
  93. singlestoredb/mysql/constants/CR.py +78 -0
  94. singlestoredb/mysql/constants/ER.py +474 -0
  95. singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
  96. singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
  97. singlestoredb/mysql/constants/FLAG.py +15 -0
  98. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  99. singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
  100. singlestoredb/mysql/constants/__init__.py +0 -0
  101. singlestoredb/mysql/converters.py +271 -0
  102. singlestoredb/mysql/cursors.py +896 -0
  103. singlestoredb/mysql/err.py +92 -0
  104. singlestoredb/mysql/optionfile.py +20 -0
  105. singlestoredb/mysql/protocol.py +450 -0
  106. singlestoredb/mysql/tests/__init__.py +19 -0
  107. singlestoredb/mysql/tests/base.py +126 -0
  108. singlestoredb/mysql/tests/conftest.py +37 -0
  109. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  110. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  111. singlestoredb/mysql/tests/test_basic.py +452 -0
  112. singlestoredb/mysql/tests/test_connection.py +851 -0
  113. singlestoredb/mysql/tests/test_converters.py +58 -0
  114. singlestoredb/mysql/tests/test_cursor.py +141 -0
  115. singlestoredb/mysql/tests/test_err.py +16 -0
  116. singlestoredb/mysql/tests/test_issues.py +514 -0
  117. singlestoredb/mysql/tests/test_load_local.py +75 -0
  118. singlestoredb/mysql/tests/test_nextset.py +88 -0
  119. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  120. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  121. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  122. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  123. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  124. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  125. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  126. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  127. singlestoredb/mysql/times.py +23 -0
  128. singlestoredb/notebook/__init__.py +16 -0
  129. singlestoredb/notebook/_objects.py +213 -0
  130. singlestoredb/notebook/_portal.py +352 -0
  131. singlestoredb/py.typed +0 -0
  132. singlestoredb/pytest.py +352 -0
  133. singlestoredb/server/__init__.py +0 -0
  134. singlestoredb/server/docker.py +452 -0
  135. singlestoredb/server/free_tier.py +267 -0
  136. singlestoredb/tests/__init__.py +0 -0
  137. singlestoredb/tests/alltypes.sql +307 -0
  138. singlestoredb/tests/alltypes_no_nulls.sql +208 -0
  139. singlestoredb/tests/empty.sql +0 -0
  140. singlestoredb/tests/ext_funcs/__init__.py +702 -0
  141. singlestoredb/tests/local_infile.csv +3 -0
  142. singlestoredb/tests/test.ipynb +18 -0
  143. singlestoredb/tests/test.sql +680 -0
  144. singlestoredb/tests/test2.ipynb +18 -0
  145. singlestoredb/tests/test2.sql +1 -0
  146. singlestoredb/tests/test_basics.py +1332 -0
  147. singlestoredb/tests/test_config.py +318 -0
  148. singlestoredb/tests/test_connection.py +3103 -0
  149. singlestoredb/tests/test_dbapi.py +27 -0
  150. singlestoredb/tests/test_exceptions.py +45 -0
  151. singlestoredb/tests/test_ext_func.py +1472 -0
  152. singlestoredb/tests/test_ext_func_data.py +1101 -0
  153. singlestoredb/tests/test_fusion.py +1527 -0
  154. singlestoredb/tests/test_http.py +288 -0
  155. singlestoredb/tests/test_management.py +1599 -0
  156. singlestoredb/tests/test_plugin.py +33 -0
  157. singlestoredb/tests/test_results.py +171 -0
  158. singlestoredb/tests/test_types.py +132 -0
  159. singlestoredb/tests/test_udf.py +737 -0
  160. singlestoredb/tests/test_udf_returns.py +459 -0
  161. singlestoredb/tests/test_vectorstore.py +51 -0
  162. singlestoredb/tests/test_xdict.py +333 -0
  163. singlestoredb/tests/utils.py +141 -0
  164. singlestoredb/types.py +373 -0
  165. singlestoredb/utils/__init__.py +0 -0
  166. singlestoredb/utils/config.py +950 -0
  167. singlestoredb/utils/convert_rows.py +69 -0
  168. singlestoredb/utils/debug.py +13 -0
  169. singlestoredb/utils/dtypes.py +205 -0
  170. singlestoredb/utils/events.py +65 -0
  171. singlestoredb/utils/mogrify.py +151 -0
  172. singlestoredb/utils/results.py +585 -0
  173. singlestoredb/utils/xdict.py +425 -0
  174. singlestoredb/vectorstore.py +192 -0
  175. singlestoredb/warnings.py +5 -0
  176. singlestoredb-1.16.1.dist-info/METADATA +165 -0
  177. singlestoredb-1.16.1.dist-info/RECORD +183 -0
  178. singlestoredb-1.16.1.dist-info/WHEEL +5 -0
  179. singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
  180. singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
  181. singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
  182. sqlx/__init__.py +4 -0
  183. sqlx/magic.py +113 -0
@@ -0,0 +1,514 @@
1
+ # type: ignore
2
+ import datetime
3
+ import time
4
+ import warnings
5
+
6
+ import pytest
7
+
8
+ import singlestoredb.mysql as sv
9
+ from singlestoredb.mysql.tests import base
10
+
11
+ __all__ = ['TestOldIssues', 'TestNewIssues', 'TestGitHubIssues']
12
+
13
+
14
+ class TestOldIssues(base.PyMySQLTestCase):
15
+
16
+ def test_issue_3(self):
17
+ """Undefined methods datetime_or_None, date_or_None."""
18
+ conn = self.connect()
19
+ c = conn.cursor()
20
+ with warnings.catch_warnings():
21
+ warnings.filterwarnings('ignore')
22
+ c.execute('drop table if exists issue3')
23
+ c.execute('create table issue3 (d date, t time, dt datetime, ts timestamp)')
24
+ try:
25
+ c.execute(
26
+ 'insert into issue3 (d, t, dt, ts) values (%s,%s,%s,%s)',
27
+ (None, None, None, None),
28
+ )
29
+ c.execute('select d from issue3')
30
+ self.assertEqual(None, c.fetchone()[0])
31
+ c.execute('select t from issue3')
32
+ self.assertEqual(None, c.fetchone()[0])
33
+ c.execute('select dt from issue3')
34
+ self.assertEqual(None, c.fetchone()[0])
35
+ c.execute('select ts from issue3')
36
+ self.assertIn(
37
+ type(c.fetchone()[0]),
38
+ (type(None), datetime.datetime),
39
+ 'expected Python type None or datetime from SQL timestamp',
40
+ )
41
+ finally:
42
+ c.execute('drop table issue3')
43
+
44
+ def test_issue_4(self):
45
+ """Can't retrieve TIMESTAMP fields."""
46
+ conn = self.connect()
47
+ c = conn.cursor()
48
+ with warnings.catch_warnings():
49
+ warnings.filterwarnings('ignore')
50
+ c.execute('drop table if exists issue4')
51
+ c.execute('create table issue4 (ts timestamp)')
52
+ try:
53
+ c.execute('insert into issue4 (ts) values (now())')
54
+ c.execute('select ts from issue4')
55
+ self.assertTrue(isinstance(c.fetchone()[0], datetime.datetime))
56
+ finally:
57
+ c.execute('drop table issue4')
58
+
59
+ def test_issue_5(self):
60
+ """Query on information_schema.tables fails."""
61
+ con = self.connect()
62
+ cur = con.cursor()
63
+ cur.execute('select * from information_schema.tables')
64
+
65
+ @pytest.mark.skip(reason='database is not created')
66
+ def test_issue_6(self):
67
+ """TypeError: ord() expected a character, but string of length 0 found."""
68
+ # ToDo: this test requires access to db 'mysql'.
69
+ kwargs = self.databases[0].copy()
70
+ kwargs['database'] = 'mysql'
71
+ conn = sv.connect(**kwargs)
72
+ c = conn.cursor()
73
+ c.execute('select * from user')
74
+ conn.close()
75
+
76
+ def test_issue_8(self):
77
+ """Primary Key and Index error when selecting data."""
78
+ conn = self.connect()
79
+ c = conn.cursor()
80
+ with warnings.catch_warnings():
81
+ warnings.filterwarnings('ignore')
82
+ c.execute('drop table if exists test')
83
+ c.execute(
84
+ """CREATE TABLE `test` (`station` int NOT NULL DEFAULT '0', `dh`
85
+ datetime NOT NULL DEFAULT '2015-01-01 00:00:00', `echeance` int NOT NULL
86
+ DEFAULT '0', `me` double DEFAULT NULL, `mo` double DEFAULT NULL, PRIMARY
87
+ KEY (`station`,`dh`,`echeance`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;""",
88
+ )
89
+ try:
90
+ self.assertIn(c.execute('SELECT * FROM test'), [0, -1])
91
+ c.execute('ALTER TABLE `test` ADD INDEX `idx_station` (`station`)')
92
+ self.assertIn(c.execute('SELECT * FROM test'), [0, -1])
93
+ finally:
94
+ c.execute('drop table test')
95
+
96
+ def test_issue_13(self):
97
+ """Can't handle large result fields."""
98
+ conn = self.connect()
99
+ cur = conn.cursor()
100
+ with warnings.catch_warnings():
101
+ warnings.filterwarnings('ignore')
102
+ cur.execute('drop table if exists issue13')
103
+ try:
104
+ cur.execute('create table issue13 (t text)')
105
+ # ticket says 18k
106
+ size = 18 * 1024
107
+ cur.execute('insert into issue13 (t) values (%s)', ('x' * size,))
108
+ cur.execute('select t from issue13')
109
+ # use assertTrue so that obscenely huge error messages don't print
110
+ r = cur.fetchone()[0]
111
+ self.assertTrue('x' * size == r)
112
+ finally:
113
+ cur.execute('drop table issue13')
114
+
115
+ def test_issue_15(self):
116
+ """Query should be expanded before perform character encoding."""
117
+ conn = self.connect()
118
+ c = conn.cursor()
119
+ with warnings.catch_warnings():
120
+ warnings.filterwarnings('ignore')
121
+ c.execute('drop table if exists issue15')
122
+ c.execute('create table issue15 (t varchar(32))')
123
+ try:
124
+ c.execute('insert into issue15 (t) values (%s)', ('\xe4\xf6\xfc',))
125
+ c.execute('select t from issue15')
126
+ self.assertEqual('\xe4\xf6\xfc', c.fetchone()[0])
127
+ finally:
128
+ c.execute('drop table issue15')
129
+
130
+ def test_issue_16(self):
131
+ """Patch for string and tuple escaping."""
132
+ conn = self.connect()
133
+ c = conn.cursor()
134
+ with warnings.catch_warnings():
135
+ warnings.filterwarnings('ignore')
136
+ c.execute('drop table if exists issue16')
137
+ c.execute(
138
+ 'create table issue16 (name varchar(32) primary key, email varchar(32))',
139
+ )
140
+ try:
141
+ c.execute(
142
+ "insert into issue16 (name, email) values ('pete', 'floydophone')",
143
+ )
144
+ c.execute('select email from issue16 where name=%s', ('pete',))
145
+ self.assertEqual('floydophone', c.fetchone()[0])
146
+ finally:
147
+ c.execute('drop table issue16')
148
+
149
+ @pytest.mark.skip(
150
+ 'test_issue_17() requires a custom, legacy MySQL configuration '
151
+ 'and will not be run.',
152
+ )
153
+ def test_issue_17(self):
154
+ """Could not connect mysql use password."""
155
+ conn = self.connect()
156
+ host = self.databases[0]['host']
157
+ db = self.databases[0]['database']
158
+ c = conn.cursor()
159
+
160
+ # grant access to a table to a user with a password
161
+ try:
162
+ with warnings.catch_warnings():
163
+ warnings.filterwarnings('ignore')
164
+ c.execute('drop table if exists issue17')
165
+ c.execute('create table issue17 (x varchar(32) primary key)')
166
+ c.execute("insert into issue17 (x) values ('hello, world!')")
167
+ c.execute(
168
+ (
169
+ "grant all privileges on %s.issue17 to 'issue17user'@'%%' "
170
+ "identified by '1234'"
171
+ ) % db,
172
+ )
173
+ conn.commit()
174
+
175
+ conn2 = sv.connect(host=host, user='issue17user', passwd='1234', db=db)
176
+ c2 = conn2.cursor()
177
+ c2.execute('select x from issue17')
178
+ self.assertEqual('hello, world!', c2.fetchone()[0])
179
+ finally:
180
+ c.execute('drop table issue17')
181
+
182
+
183
+ class TestNewIssues(base.PyMySQLTestCase):
184
+
185
+ def test_issue_34(self):
186
+ try:
187
+ sv.connect(host='localhost', port=1237, user='root')
188
+ self.fail()
189
+ except sv.OperationalError as e:
190
+ self.assertEqual(2003, e.args[0])
191
+ except Exception:
192
+ self.fail()
193
+
194
+ def test_issue_33(self):
195
+ conn = sv.connect(charset='utf8', **self.databases[0])
196
+ self.safe_create_table(
197
+ conn, 'hei\xdfe', 'create table hei\xdfe (name varchar(32))',
198
+ )
199
+ c = conn.cursor()
200
+ c.execute("insert into hei\xdfe (name) values ('Pi\xdfata')")
201
+ c.execute('select name from hei\xdfe')
202
+ self.assertEqual('Pi\xdfata', c.fetchone()[0])
203
+
204
+ @pytest.mark.skip('This test requires manual intervention')
205
+ def test_issue_35(self):
206
+ conn = self.connect()
207
+ c = conn.cursor()
208
+ print('sudo killall -9 mysqld within the next 10 seconds')
209
+ try:
210
+ c.execute('select sleep(10)')
211
+ self.fail()
212
+ except sv.OperationalError as e:
213
+ self.assertEqual(2013, e.args[0])
214
+
215
+ def test_issue_36(self):
216
+ # connection 0 is super user, connection 1 isn't
217
+ conn = self.connections[1]
218
+ c = conn.cursor()
219
+ c.execute('show processlist')
220
+ kill_id = None
221
+ for row in c.fetchall():
222
+ id = row[0]
223
+ info = row[7]
224
+ if info == 'show processlist':
225
+ kill_id = id
226
+ break
227
+ self.assertEqual(kill_id, conn.thread_id())
228
+ # now nuke the connection
229
+ self.connections[0].kill(kill_id)
230
+ # make sure this connection has broken
231
+ try:
232
+ c.execute('show tables')
233
+ self.fail()
234
+ except Exception:
235
+ pass
236
+ c.close()
237
+ conn.close()
238
+
239
+ # check the process list from the other connection
240
+ try:
241
+ # Wait since Travis-CI sometimes fail this test.
242
+ time.sleep(0.1)
243
+
244
+ c = self.connections[0].cursor()
245
+ c.execute('show processlist')
246
+ ids = [row[0] for row in c.fetchall()]
247
+ self.assertFalse(kill_id in ids)
248
+ finally:
249
+ del self.connections[1]
250
+
251
+ @pytest.mark.skip(reason='@foo is not set in SingleStoreDB')
252
+ def test_issue_37(self):
253
+ conn = self.connect()
254
+ c = conn.cursor()
255
+ self.assertEqual(1, c.execute('SELECT @foo'))
256
+ self.assertEqual((None,), c.fetchone())
257
+ self.assertEqual(0, c.execute("SET @foo = 'bar'"))
258
+ c.execute("set @foo = 'bar'")
259
+
260
+ def test_issue_38(self):
261
+ conn = self.connect()
262
+ c = conn.cursor()
263
+ datum = 'a' * 1024 * 1023 # reduced size for most default mysql installs
264
+
265
+ try:
266
+ with warnings.catch_warnings():
267
+ warnings.filterwarnings('ignore')
268
+ c.execute('drop table if exists issue38')
269
+ c.execute('create table issue38 (id integer, data mediumblob)')
270
+ c.execute('insert into issue38 values (1, %s)', (datum,))
271
+ finally:
272
+ c.execute('drop table issue38')
273
+
274
+ def disabled_test_issue_54(self):
275
+ conn = self.connect()
276
+ c = conn.cursor()
277
+ with warnings.catch_warnings():
278
+ warnings.filterwarnings('ignore')
279
+ c.execute('drop table if exists issue54')
280
+ big_sql = 'select * from issue54 where '
281
+ big_sql += ' and '.join('%d=%d' % (i, i) for i in range(0, 100000))
282
+
283
+ try:
284
+ c.execute('create table issue54 (id integer primary key)')
285
+ c.execute('insert into issue54 (id) values (7)')
286
+ c.execute(big_sql)
287
+ self.assertEqual(7, c.fetchone()[0])
288
+ finally:
289
+ c.execute('drop table issue54')
290
+
291
+
292
+ class TestGitHubIssues(base.PyMySQLTestCase):
293
+
294
+ def test_issue_66(self):
295
+ """'Connection' object has no attribute 'insert_id'."""
296
+ conn = self.connect()
297
+ c = conn.cursor()
298
+ self.assertEqual(0, conn.insert_id())
299
+ try:
300
+ with warnings.catch_warnings():
301
+ warnings.filterwarnings('ignore')
302
+ c.execute('drop table if exists issue66')
303
+ c.execute(
304
+ 'create table issue66 (id integer primary key auto_increment, x integer)',
305
+ )
306
+ c.execute('insert into issue66 (x) values (1)')
307
+ c.execute('insert into issue66 (x) values (1)')
308
+ self.assertEqual(2, conn.insert_id())
309
+ finally:
310
+ c.execute('drop table issue66')
311
+
312
+ def test_issue_79(self):
313
+ """Duplicate field overwrites the previous one in the result of DictCursor."""
314
+ conn = self.connect(cursorclass=sv.cursors.DictCursor)
315
+ c = conn.cursor()
316
+
317
+ with warnings.catch_warnings():
318
+ warnings.filterwarnings('ignore')
319
+ c.execute('drop table if exists a')
320
+ c.execute('drop table if exists b')
321
+ c.execute("""CREATE TABLE a (id int, value int)""")
322
+ c.execute("""CREATE TABLE b (id int, value int)""")
323
+
324
+ a = (1, 11)
325
+ b = (1, 22)
326
+ try:
327
+ c.execute('insert into a values (%s, %s)', a)
328
+ c.execute('insert into b values (%s, %s)', b)
329
+
330
+ c.execute('SELECT * FROM a inner join b on a.id = b.id')
331
+ r = c.fetchall()[0]
332
+ self.assertEqual(r['id'], 1)
333
+ self.assertEqual(r['value'], 11)
334
+ self.assertEqual(r['b.value'], 22)
335
+ finally:
336
+ c.execute('drop table a')
337
+ c.execute('drop table b')
338
+
339
+ def test_issue_95(self):
340
+ """Leftover trailing OK packet for "CALL my_sp" queries."""
341
+ conn = self.connect()
342
+ cur = conn.cursor()
343
+ with warnings.catch_warnings():
344
+ warnings.filterwarnings('ignore')
345
+ cur.execute('DROP PROCEDURE IF EXISTS `foo`')
346
+ cur.execute(
347
+ """CREATE PROCEDURE `foo` () AS
348
+ BEGIN
349
+ ECHO SELECT 1;
350
+ END""",
351
+ )
352
+ try:
353
+ cur.execute("""CALL foo()""")
354
+ cur.execute("""SELECT 1""")
355
+ self.assertEqual(cur.fetchone()[0], 1)
356
+ finally:
357
+ with warnings.catch_warnings():
358
+ warnings.filterwarnings('ignore')
359
+ cur.execute('DROP PROCEDURE IF EXISTS `foo`')
360
+
361
+ def test_issue_114(self):
362
+ """Autocommit is not set after reconnecting with ping()."""
363
+ conn = sv.connect(charset='utf8', **self.databases[0])
364
+ conn.autocommit(False)
365
+ c = conn.cursor()
366
+
367
+ if type(c).__name__.startswith('SS'):
368
+ self.skipTest('Test hangs with unbuffered cursor')
369
+
370
+ c.execute("""select @@autocommit;""")
371
+ self.assertFalse(c.fetchone()[0])
372
+ conn.close()
373
+ conn.ping()
374
+ c.execute("""select @@autocommit;""")
375
+ self.assertFalse(c.fetchone()[0])
376
+ conn.close()
377
+
378
+ # Ensure autocommit() is still working
379
+ conn = sv.connect(charset='utf8', **self.databases[0])
380
+ c = conn.cursor()
381
+ c.execute("""select @@autocommit;""")
382
+ self.assertFalse(c.fetchone()[0])
383
+ conn.close()
384
+ conn.ping()
385
+ conn.autocommit(True)
386
+ c.execute("""select @@autocommit;""")
387
+ self.assertTrue(c.fetchone()[0])
388
+ conn.close()
389
+
390
+ def test_issue_175(self):
391
+ """The number of fields returned by server is read in wrong way."""
392
+ conn = self.connect()
393
+ cur = conn.cursor()
394
+ for length in (200, 300):
395
+ columns = ', '.join('c{0} integer'.format(i) for i in range(length))
396
+ sql = 'create table test_field_count ({0})'.format(columns)
397
+ try:
398
+ cur.execute(sql)
399
+ cur.execute('select * from test_field_count')
400
+ assert len(cur.description) == length
401
+ finally:
402
+ with warnings.catch_warnings():
403
+ warnings.filterwarnings('ignore')
404
+ cur.execute('drop table if exists test_field_count')
405
+
406
+ def test_issue_321(self):
407
+ """Test iterable as query argument."""
408
+ conn = sv.connect(charset='utf8', **self.databases[0])
409
+ self.safe_create_table(
410
+ conn,
411
+ 'issue321',
412
+ 'create table issue321 (value_1 varchar(1), value_2 varchar(1))',
413
+ )
414
+
415
+ sql_insert = 'insert into issue321 (value_1, value_2) values (%s, %s)'
416
+ sql_dict_insert = (
417
+ 'insert into issue321 (value_1, value_2) '
418
+ 'values (%(value_1)s, %(value_2)s)'
419
+ )
420
+ sql_select = 'select * from issue321 ' + \
421
+ 'where value_1 in %s and value_2=%s order by value_1'
422
+ data = [
423
+ [('a',), '\u0430'],
424
+ [['b'], '\u0430'],
425
+ {'value_1': [['c']], 'value_2': '\u0430'},
426
+ ]
427
+ cur = conn.cursor()
428
+ self.assertEqual(cur.execute(sql_insert, data[0]), 1)
429
+ self.assertEqual(cur.execute(sql_insert, data[1]), 1)
430
+ self.assertEqual(cur.execute(sql_dict_insert, data[2]), 1)
431
+ self.assertIn(cur.execute(sql_select, [('a', 'b', 'c'), '\u0430']), [3, -1])
432
+ self.assertEqual(cur.fetchone(), ('a', '\u0430'))
433
+ self.assertEqual(cur.fetchone(), ('b', '\u0430'))
434
+ self.assertEqual(cur.fetchone(), ('c', '\u0430'))
435
+
436
+ def test_issue_364(self):
437
+ """Test mixed unicode/binary arguments in executemany."""
438
+ conn = sv.connect(charset='utf8mb4', **self.databases[0])
439
+ self.safe_create_table(
440
+ conn,
441
+ 'issue364',
442
+ 'create table issue364 (value_1 binary(3), value_2 varchar(3)) '
443
+ 'engine=InnoDB default charset=utf8mb4',
444
+ )
445
+
446
+ sql = 'insert into issue364 (value_1, value_2) values (_binary %s, %s)'
447
+ usql = 'insert into issue364 (value_1, value_2) values (_binary %s, %s)'
448
+ values = [sv.Binary(b'\x00\xff\x00'), '\xe4\xf6\xfc']
449
+
450
+ # test single insert and select
451
+ cur = conn.cursor()
452
+ cur.execute(sql, args=values)
453
+ cur.execute('select * from issue364')
454
+ self.assertEqual(cur.fetchone(), tuple(values))
455
+
456
+ # test single insert unicode query
457
+ cur.execute(usql, args=values)
458
+
459
+ # test multi insert and select
460
+ cur.executemany(sql, args=(values, values, values))
461
+ cur.execute('select * from issue364')
462
+ for row in cur.fetchall():
463
+ self.assertEqual(row, tuple(values))
464
+
465
+ # test multi insert with unicode query
466
+ cur.executemany(usql, args=(values, values, values))
467
+
468
+ @pytest.mark.skip(reason='syntax not supported by SingleStoreDB')
469
+ def test_issue_363(self):
470
+ """Test binary / geometry types."""
471
+ conn = sv.connect(charset='utf8', **self.databases[0])
472
+ self.safe_create_table(
473
+ conn,
474
+ 'issue363',
475
+ 'CREATE TABLE issue363 ( '
476
+ 'id INTEGER PRIMARY KEY, geom LINESTRING NOT NULL /*!80003 SRID 0 */, '
477
+ 'SPATIAL KEY geom (geom)) '
478
+ 'ENGINE=MyISAM',
479
+ )
480
+
481
+ cur = conn.cursor()
482
+ query = (
483
+ 'INSERT INTO issue363 (id, geom) VALUES'
484
+ "(1998, ST_GeomFromText('LINESTRING(1.1 1.1,2.2 2.2)'))"
485
+ )
486
+ cur.execute(query)
487
+
488
+ # select WKT
489
+ query = 'SELECT ST_AsText(geom) FROM issue363'
490
+ cur.execute(query)
491
+ row = cur.fetchone()
492
+ self.assertEqual(row, ('LINESTRING(1.1 1.1,2.2 2.2)',))
493
+
494
+ # select WKB
495
+ query = 'SELECT ST_AsBinary(geom) FROM issue363'
496
+ cur.execute(query)
497
+ row = cur.fetchone()
498
+ self.assertEqual(
499
+ row,
500
+ (
501
+ b'\x01\x02\x00\x00\x00\x02\x00\x00\x00'
502
+ b'\x9a\x99\x99\x99\x99\x99\xf1?'
503
+ b'\x9a\x99\x99\x99\x99\x99\xf1?'
504
+ b'\x9a\x99\x99\x99\x99\x99\x01@'
505
+ b'\x9a\x99\x99\x99\x99\x99\x01@',
506
+ ),
507
+ )
508
+
509
+ # select internal binary
510
+ cur.execute('SELECT geom FROM issue363')
511
+ row = cur.fetchone()
512
+ # don't assert the exact internal binary value, as it could
513
+ # vary across implementations
514
+ self.assertTrue(isinstance(row[0], bytes))
@@ -0,0 +1,75 @@
1
+ # type: ignore
2
+ import os
3
+
4
+ from singlestoredb.mysql import cursors
5
+ from singlestoredb.mysql import OperationalError
6
+ from singlestoredb.mysql.tests import base
7
+
8
+ __all__ = ['TestLoadLocal']
9
+
10
+
11
+ class TestLoadLocal(base.PyMySQLTestCase):
12
+
13
+ def test_no_file(self):
14
+ """Test load local infile when the file does not exist."""
15
+ conn = self.connect()
16
+ c = conn.cursor()
17
+ c.execute('CREATE TABLE test_load_local (a INTEGER, b INTEGER, c TEXT)')
18
+ try:
19
+ self.assertRaises(
20
+ OperationalError,
21
+ c.execute,
22
+ (
23
+ "LOAD DATA LOCAL INFILE 'no_data.txt' INTO TABLE "
24
+ "test_load_local fields terminated by ','"
25
+ ),
26
+ )
27
+ finally:
28
+ c.execute('DROP TABLE test_load_local')
29
+ c.close()
30
+
31
+ def test_load_file(self):
32
+ """Test load local infile with a valid file."""
33
+ conn = self.connect()
34
+ c = conn.cursor()
35
+ c.execute('CREATE TABLE test_load_local (a INTEGER, b INTEGER, c TEXT)')
36
+ filename = os.path.join(
37
+ os.path.dirname(os.path.realpath(__file__)), 'data', 'load_local_data.txt',
38
+ )
39
+ try:
40
+ c.execute(
41
+ f"LOAD DATA LOCAL INFILE '{filename}' INTO TABLE test_load_local "
42
+ "FIELDS TERMINATED BY ','",
43
+ )
44
+ c.execute('SELECT COUNT(*) FROM test_load_local')
45
+ self.assertEqual(22749, c.fetchone()[0])
46
+ finally:
47
+ c.execute('DROP TABLE test_load_local')
48
+
49
+ def test_unbuffered_load_file(self):
50
+ """Test unbuffered load local infile with a valid file."""
51
+ conn = self.connect(cursorclass=cursors.SSCursor)
52
+ c = conn.cursor()
53
+ c.execute('CREATE TABLE test_load_local (a INTEGER, b INTEGER, c TEXT)')
54
+ filename = os.path.join(
55
+ os.path.dirname(os.path.realpath(__file__)), 'data', 'load_local_data.txt',
56
+ )
57
+ try:
58
+ c.execute(
59
+ f"LOAD DATA LOCAL INFILE '{filename}' INTO TABLE test_load_local "
60
+ "FIELDS TERMINATED BY ','",
61
+ )
62
+ c.execute('SELECT COUNT(*) FROM test_load_local')
63
+ self.assertEqual(22749, c.fetchone()[0])
64
+ finally:
65
+ c.close()
66
+ conn.close()
67
+ conn.connect()
68
+ c = conn.cursor()
69
+ c.execute('DROP TABLE test_load_local')
70
+
71
+
72
+ if __name__ == '__main__':
73
+ import unittest
74
+
75
+ unittest.main()
@@ -0,0 +1,88 @@
1
+ # type: ignore
2
+ import pytest
3
+
4
+ import singlestoredb.mysql as sv
5
+ from singlestoredb.mysql.constants import CLIENT
6
+ from singlestoredb.mysql.tests import base
7
+
8
+
9
+ class TestNextset(base.PyMySQLTestCase):
10
+
11
+ def test_nextset(self):
12
+ con = self.connect(
13
+ init_command='SELECT "bar"; SELECT "baz"',
14
+ client_flag=CLIENT.MULTI_STATEMENTS,
15
+ )
16
+ cur = con.cursor()
17
+ cur.execute('SELECT 1; SELECT 2;')
18
+ self.assertEqual([(1,)], list(cur))
19
+
20
+ r = cur.nextset()
21
+ self.assertTrue(r)
22
+
23
+ self.assertEqual([(2,)], list(cur))
24
+ self.assertIsNone(cur.nextset())
25
+
26
+ def test_skip_nextset(self):
27
+ cur = self.connect(client_flag=CLIENT.MULTI_STATEMENTS).cursor()
28
+ cur.execute('SELECT 1; SELECT 2;')
29
+ self.assertEqual([(1,)], list(cur))
30
+
31
+ cur.execute('SELECT 42')
32
+ self.assertEqual([(42,)], list(cur))
33
+
34
+ def test_nextset_error(self):
35
+ con = self.connect(client_flag=CLIENT.MULTI_STATEMENTS)
36
+ cur = con.cursor()
37
+
38
+ if type(cur).__name__.startswith('SS'):
39
+ self.skipTest('nextset in unbuffered cursor closes connection')
40
+
41
+ for i in range(3):
42
+ cur.execute('SELECT %s; xyzzy;', (i,))
43
+ self.assertEqual([(i,)], list(cur))
44
+ with self.assertRaises(sv.ProgrammingError):
45
+ cur.nextset()
46
+ self.assertEqual((), cur.fetchall())
47
+
48
+ def test_ok_and_next(self):
49
+ cur = self.connect(client_flag=CLIENT.MULTI_STATEMENTS).cursor()
50
+ cur.execute('SELECT 1; commit; SELECT 2;')
51
+ self.assertEqual([(1,)], list(cur))
52
+ self.assertTrue(cur.nextset())
53
+ self.assertTrue(cur.nextset())
54
+ self.assertEqual([(2,)], list(cur))
55
+ self.assertFalse(bool(cur.nextset()))
56
+
57
+ @pytest.mark.xfail
58
+ def test_multi_cursor(self):
59
+ con = self.connect(client_flag=CLIENT.MULTI_STATEMENTS)
60
+ cur1 = con.cursor()
61
+ cur2 = con.cursor()
62
+
63
+ cur1.execute('SELECT 1; SELECT 2;')
64
+ cur2.execute('SELECT 42')
65
+
66
+ self.assertEqual([(1,)], list(cur1))
67
+ self.assertEqual([(42,)], list(cur2))
68
+
69
+ r = cur1.nextset()
70
+ self.assertTrue(r)
71
+
72
+ self.assertEqual([(2,)], list(cur1))
73
+ self.assertIsNone(cur1.nextset())
74
+
75
+ def test_multi_statement_warnings(self):
76
+ con = self.connect(
77
+ init_command='SELECT "bar"; SELECT "baz"',
78
+ client_flag=CLIENT.MULTI_STATEMENTS,
79
+ )
80
+ cursor = con.cursor()
81
+
82
+ try:
83
+ cursor.execute('DROP TABLE IF EXISTS a; ' 'DROP TABLE IF EXISTS b;')
84
+ except TypeError:
85
+ self.fail()
86
+
87
+ # TODO: How about SSCursor and nextset?
88
+ # It's very hard to implement correctly...