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,465 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# type: ignore
|
|
3
|
+
"""SingleStoreDB Fusion testing."""
|
|
4
|
+
import os
|
|
5
|
+
import random
|
|
6
|
+
import secrets
|
|
7
|
+
import time
|
|
8
|
+
import unittest
|
|
9
|
+
from typing import Any
|
|
10
|
+
from typing import List
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
import singlestoredb as s2
|
|
15
|
+
from singlestoredb.tests import utils
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestFusion(unittest.TestCase):
|
|
19
|
+
|
|
20
|
+
dbname: str = ''
|
|
21
|
+
dbexisted: bool = False
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def setUpClass(cls):
|
|
25
|
+
sql_file = os.path.join(os.path.dirname(__file__), 'test.sql')
|
|
26
|
+
cls.dbname, cls.dbexisted = utils.load_sql(sql_file)
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def tearDownClass(cls):
|
|
30
|
+
if not cls.dbexisted:
|
|
31
|
+
utils.drop_database(cls.dbname)
|
|
32
|
+
|
|
33
|
+
def setUp(self):
|
|
34
|
+
self.enabled = os.environ.get('SINGLESTOREDB_FUSION_ENABLED')
|
|
35
|
+
os.environ['SINGLESTOREDB_FUSION_ENABLED'] = '1'
|
|
36
|
+
self.conn = s2.connect(database=type(self).dbname, local_infile=True)
|
|
37
|
+
self.cur = self.conn.cursor()
|
|
38
|
+
|
|
39
|
+
def tearDown(self):
|
|
40
|
+
if self.enabled:
|
|
41
|
+
os.environ['SINGLESTOREDB_FUSION_ENABLED'] = self.enabled
|
|
42
|
+
else:
|
|
43
|
+
del os.environ['SINGLESTOREDB_FUSION_ENABLED']
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
if self.cur is not None:
|
|
47
|
+
self.cur.close()
|
|
48
|
+
except Exception:
|
|
49
|
+
# traceback.print_exc()
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
if self.conn is not None:
|
|
54
|
+
self.conn.close()
|
|
55
|
+
except Exception:
|
|
56
|
+
# traceback.print_exc()
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
def test_env_var(self):
|
|
60
|
+
os.environ['SINGLESTOREDB_FUSION_ENABLED'] = '0'
|
|
61
|
+
|
|
62
|
+
with self.assertRaises(s2.ProgrammingError):
|
|
63
|
+
self.cur.execute('show fusion commands')
|
|
64
|
+
|
|
65
|
+
del os.environ['SINGLESTOREDB_FUSION_ENABLED']
|
|
66
|
+
|
|
67
|
+
with self.assertRaises(s2.ProgrammingError):
|
|
68
|
+
self.cur.execute('show fusion commands')
|
|
69
|
+
|
|
70
|
+
os.environ['SINGLESTOREDB_FUSION_ENABLED'] = 'yes'
|
|
71
|
+
|
|
72
|
+
self.cur.execute('show fusion commands')
|
|
73
|
+
assert list(self.cur)
|
|
74
|
+
|
|
75
|
+
def test_show_commands(self):
|
|
76
|
+
self.cur.execute('show fusion commands')
|
|
77
|
+
cmds = [x[0] for x in self.cur.fetchall()]
|
|
78
|
+
assert cmds
|
|
79
|
+
assert [x for x in cmds if x.strip().startswith('SHOW FUSION GRAMMAR')], cmds
|
|
80
|
+
|
|
81
|
+
self.cur.execute('show fusion commands like "create%"')
|
|
82
|
+
cmds = [x[0] for x in self.cur.fetchall()]
|
|
83
|
+
assert cmds
|
|
84
|
+
assert [x for x in cmds if x.strip().startswith('CREATE')] == cmds, cmds
|
|
85
|
+
|
|
86
|
+
def test_show_grammar(self):
|
|
87
|
+
self.cur.execute('show fusion grammar for "create workspace"')
|
|
88
|
+
cmds = [x[0] for x in self.cur.fetchall()]
|
|
89
|
+
assert cmds
|
|
90
|
+
assert [x for x in cmds if x.strip().startswith('CREATE WORKSPACE')], cmds
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@pytest.mark.management
|
|
94
|
+
class TestManagementAPIFusion(unittest.TestCase):
|
|
95
|
+
|
|
96
|
+
id: str = secrets.token_hex(8)
|
|
97
|
+
dbname: str = ''
|
|
98
|
+
dbexisted: bool = False
|
|
99
|
+
workspace_groups: List[Any] = []
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def setUpClass(cls):
|
|
103
|
+
sql_file = os.path.join(os.path.dirname(__file__), 'test.sql')
|
|
104
|
+
cls.dbname, cls.dbexisted = utils.load_sql(sql_file)
|
|
105
|
+
mgr = s2.manage_workspaces()
|
|
106
|
+
us_regions = [x for x in mgr.regions if x.name.startswith('US')]
|
|
107
|
+
non_us_regions = [x for x in mgr.regions if not x.name.startswith('US')]
|
|
108
|
+
wg = mgr.create_workspace_group(
|
|
109
|
+
f'A Fusion Testing {cls.id}',
|
|
110
|
+
region=random.choice(us_regions),
|
|
111
|
+
firewall_ranges=[],
|
|
112
|
+
)
|
|
113
|
+
cls.workspace_groups.append(wg)
|
|
114
|
+
wg = mgr.create_workspace_group(
|
|
115
|
+
f'B Fusion Testing {cls.id}',
|
|
116
|
+
region=random.choice(us_regions),
|
|
117
|
+
firewall_ranges=[],
|
|
118
|
+
)
|
|
119
|
+
cls.workspace_groups.append(wg)
|
|
120
|
+
wg = mgr.create_workspace_group(
|
|
121
|
+
f'C Fusion Testing {cls.id}',
|
|
122
|
+
region=random.choice(non_us_regions),
|
|
123
|
+
firewall_ranges=[],
|
|
124
|
+
)
|
|
125
|
+
cls.workspace_groups.append(wg)
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def tearDownClass(cls):
|
|
129
|
+
if not cls.dbexisted:
|
|
130
|
+
utils.drop_database(cls.dbname)
|
|
131
|
+
while cls.workspace_groups:
|
|
132
|
+
cls.workspace_groups.pop().terminate(force=True)
|
|
133
|
+
|
|
134
|
+
def setUp(self):
|
|
135
|
+
self.enabled = os.environ.get('SINGLESTOREDB_FUSION_ENABLED')
|
|
136
|
+
os.environ['SINGLESTOREDB_FUSION_ENABLED'] = '1'
|
|
137
|
+
self.conn = s2.connect(database=type(self).dbname, local_infile=True)
|
|
138
|
+
self.cur = self.conn.cursor()
|
|
139
|
+
|
|
140
|
+
def tearDown(self):
|
|
141
|
+
if self.enabled:
|
|
142
|
+
os.environ['SINGLESTOREDB_FUSION_ENABLED'] = self.enabled
|
|
143
|
+
else:
|
|
144
|
+
del os.environ['SINGLESTOREDB_FUSION_ENABLED']
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
if self.cur is not None:
|
|
148
|
+
self.cur.close()
|
|
149
|
+
except Exception:
|
|
150
|
+
# traceback.print_exc()
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
if self.conn is not None:
|
|
155
|
+
self.conn.close()
|
|
156
|
+
except Exception:
|
|
157
|
+
# traceback.print_exc()
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
def test_show_regions(self):
|
|
161
|
+
self.cur.execute('show regions')
|
|
162
|
+
regs = list(self.cur)
|
|
163
|
+
desc = self.cur.description
|
|
164
|
+
|
|
165
|
+
us_regs = [x for x in regs if x[0].startswith('US')]
|
|
166
|
+
|
|
167
|
+
assert len(desc) == 3
|
|
168
|
+
assert len(regs) > 5
|
|
169
|
+
assert len(us_regs) > 5
|
|
170
|
+
|
|
171
|
+
# LIKE
|
|
172
|
+
self.cur.execute('show regions like "US%"')
|
|
173
|
+
regs = list(self.cur)
|
|
174
|
+
assert regs == us_regs
|
|
175
|
+
|
|
176
|
+
# LIMIT
|
|
177
|
+
self.cur.execute('show regions like "US%" limit 3')
|
|
178
|
+
regs = list(self.cur)
|
|
179
|
+
assert len(regs) == 3
|
|
180
|
+
|
|
181
|
+
# ORDER BY
|
|
182
|
+
self.cur.execute('show regions like "US%" limit 3 order by name')
|
|
183
|
+
regs = list(self.cur)
|
|
184
|
+
assert len(regs) == 3
|
|
185
|
+
assert regs == list(sorted(regs, key=lambda x: x[0]))
|
|
186
|
+
|
|
187
|
+
# Wrong column
|
|
188
|
+
with self.assertRaises(KeyError):
|
|
189
|
+
self.cur.execute('show regions like "US%" limit 3 order by foo')
|
|
190
|
+
|
|
191
|
+
def test_show_workspace_groups(self):
|
|
192
|
+
self.cur.execute('show workspace groups')
|
|
193
|
+
wgs = list(self.cur)
|
|
194
|
+
desc = self.cur.description
|
|
195
|
+
|
|
196
|
+
assert len(desc) == 4
|
|
197
|
+
assert desc[0].name == 'Name'
|
|
198
|
+
assert desc[1].name == 'ID'
|
|
199
|
+
assert desc[2].name == 'Region'
|
|
200
|
+
assert desc[3].name == 'FirewallRanges'
|
|
201
|
+
assert len(wgs) >= 3
|
|
202
|
+
|
|
203
|
+
names = [x[0] for x in wgs]
|
|
204
|
+
assert f'A Fusion Testing {self.id}' in names
|
|
205
|
+
assert f'B Fusion Testing {self.id}' in names
|
|
206
|
+
assert f'C Fusion Testing {self.id}' in names
|
|
207
|
+
|
|
208
|
+
# LIKE clause
|
|
209
|
+
self.cur.execute(f'show workspace groups like "A%sion Testing {self.id}"')
|
|
210
|
+
wgs = list(self.cur)
|
|
211
|
+
|
|
212
|
+
names = [x[0] for x in wgs]
|
|
213
|
+
assert f'A Fusion Testing {self.id}' in names
|
|
214
|
+
assert f'B Fusion Testing {self.id}' not in names
|
|
215
|
+
assert f'C Fusion Testing {self.id}' not in names
|
|
216
|
+
|
|
217
|
+
# LIMIT clause
|
|
218
|
+
self.cur.execute('show workspace groups limit 2')
|
|
219
|
+
wgs = list(self.cur)
|
|
220
|
+
assert len(wgs) == 2
|
|
221
|
+
|
|
222
|
+
# EXTENDED attributes
|
|
223
|
+
self.cur.execute('show workspace groups extended')
|
|
224
|
+
wgs = list(self.cur)
|
|
225
|
+
desc = self.cur.description
|
|
226
|
+
|
|
227
|
+
assert len(desc) == 6
|
|
228
|
+
assert desc[4].name == 'CreatedAt'
|
|
229
|
+
assert desc[5].name == 'TerminatedAt'
|
|
230
|
+
|
|
231
|
+
# ORDER BY
|
|
232
|
+
self.cur.execute(
|
|
233
|
+
f'show workspace groups like "% Fusion Testing {self.id}" order by name desc',
|
|
234
|
+
)
|
|
235
|
+
wgs = list(self.cur)
|
|
236
|
+
|
|
237
|
+
names = [x[0] for x in wgs]
|
|
238
|
+
assert names == [
|
|
239
|
+
f'C Fusion Testing {self.id}',
|
|
240
|
+
f'B Fusion Testing {self.id}',
|
|
241
|
+
f'A Fusion Testing {self.id}',
|
|
242
|
+
]
|
|
243
|
+
|
|
244
|
+
# All options
|
|
245
|
+
self.cur.execute(
|
|
246
|
+
f'show workspace groups like "% Fusion Testing {self.id}" '
|
|
247
|
+
'extended order by name desc limit 2',
|
|
248
|
+
)
|
|
249
|
+
wgs = list(self.cur)
|
|
250
|
+
desc = self.cur.description
|
|
251
|
+
names = [x[0] for x in wgs]
|
|
252
|
+
|
|
253
|
+
assert len(desc) == 6
|
|
254
|
+
assert names == [f'C Fusion Testing {self.id}', f'B Fusion Testing {self.id}']
|
|
255
|
+
|
|
256
|
+
def test_show_workspaces(self):
|
|
257
|
+
mgr = s2.manage_workspaces()
|
|
258
|
+
wg = mgr.workspace_groups[f'B Fusion Testing {self.id}']
|
|
259
|
+
|
|
260
|
+
self.cur.execute(
|
|
261
|
+
'create workspace show-ws-1 in group '
|
|
262
|
+
f'"B Fusion Testing {self.id}" with size S-00',
|
|
263
|
+
)
|
|
264
|
+
self.cur.execute(
|
|
265
|
+
'create workspace show-ws-2 in group '
|
|
266
|
+
f'"B Fusion Testing {self.id}" with size S-00',
|
|
267
|
+
)
|
|
268
|
+
self.cur.execute(
|
|
269
|
+
'create workspace show-ws-3 in group '
|
|
270
|
+
f'"B Fusion Testing {self.id}" with size S-00',
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
time.sleep(30)
|
|
274
|
+
iterations = 20
|
|
275
|
+
while True:
|
|
276
|
+
wgs = wg.workspaces
|
|
277
|
+
states = [
|
|
278
|
+
x.state for x in wgs
|
|
279
|
+
if x.name in ('show-ws-1', 'show-ws-2', 'show-ws-3')
|
|
280
|
+
]
|
|
281
|
+
if len(states) == 3 and states.count('ACTIVE') == 3:
|
|
282
|
+
break
|
|
283
|
+
iterations -= 1
|
|
284
|
+
if not iterations:
|
|
285
|
+
raise RuntimeError('timed out waiting for workspaces to start')
|
|
286
|
+
time.sleep(30)
|
|
287
|
+
|
|
288
|
+
# SHOW
|
|
289
|
+
self.cur.execute(f'show workspaces in group "B Fusion Testing {self.id}"')
|
|
290
|
+
desc = self.cur.description
|
|
291
|
+
out = list(self.cur)
|
|
292
|
+
names = [x[0] for x in out]
|
|
293
|
+
assert len(desc) == 4
|
|
294
|
+
assert [x[0] for x in desc] == ['Name', 'ID', 'Size', 'State']
|
|
295
|
+
assert len(out) >= 3
|
|
296
|
+
assert 'show-ws-1' in names
|
|
297
|
+
assert 'show-ws-2' in names
|
|
298
|
+
assert 'show-ws-3' in names
|
|
299
|
+
|
|
300
|
+
# SHOW ID
|
|
301
|
+
self.cur.execute(f'show workspaces in group id {wg.id}')
|
|
302
|
+
desc = self.cur.description
|
|
303
|
+
out = list(self.cur)
|
|
304
|
+
names = [x[0] for x in out]
|
|
305
|
+
assert len(desc) == 4
|
|
306
|
+
assert [x[0] for x in desc] == ['Name', 'ID', 'Size', 'State']
|
|
307
|
+
assert len(out) >= 3
|
|
308
|
+
assert 'show-ws-1' in names
|
|
309
|
+
assert 'show-ws-2' in names
|
|
310
|
+
assert 'show-ws-3' in names
|
|
311
|
+
|
|
312
|
+
# LIKE clause
|
|
313
|
+
self.cur.execute(
|
|
314
|
+
'show workspaces in group '
|
|
315
|
+
f'"B Fusion Testing {self.id}" like "%2"',
|
|
316
|
+
)
|
|
317
|
+
out = list(self.cur)
|
|
318
|
+
names = [x[0] for x in out]
|
|
319
|
+
assert len(out) >= 1
|
|
320
|
+
assert [x for x in names if x.endswith('2')]
|
|
321
|
+
assert 'show-ws-1' not in names
|
|
322
|
+
assert 'show-ws-2' in names
|
|
323
|
+
assert 'show-ws-3' not in names
|
|
324
|
+
|
|
325
|
+
# Extended attributes
|
|
326
|
+
self.cur.execute(
|
|
327
|
+
'show workspaces in group '
|
|
328
|
+
f'"B Fusion Testing {self.id}" extended',
|
|
329
|
+
)
|
|
330
|
+
desc = self.cur.description
|
|
331
|
+
out = list(self.cur)
|
|
332
|
+
assert len(desc) == 7
|
|
333
|
+
assert [x[0] for x in desc] == [
|
|
334
|
+
'Name', 'ID', 'Size', 'State',
|
|
335
|
+
'Endpoint', 'CreatedAt', 'TerminatedAt',
|
|
336
|
+
]
|
|
337
|
+
|
|
338
|
+
# ORDER BY
|
|
339
|
+
self.cur.execute(
|
|
340
|
+
'show workspaces in group '
|
|
341
|
+
f'"B Fusion Testing {self.id}" order by name desc',
|
|
342
|
+
)
|
|
343
|
+
out = list(self.cur)
|
|
344
|
+
desc = self.cur.description
|
|
345
|
+
assert len(desc) == 4
|
|
346
|
+
names = [x[0] for x in out]
|
|
347
|
+
assert names == ['show-ws-3', 'show-ws-2', 'show-ws-1']
|
|
348
|
+
|
|
349
|
+
# LIMIT clause
|
|
350
|
+
self.cur.execute(
|
|
351
|
+
'show workspaces in group '
|
|
352
|
+
f'"B Fusion Testing {self.id}" order by name desc limit 2',
|
|
353
|
+
)
|
|
354
|
+
out = list(self.cur)
|
|
355
|
+
desc = self.cur.description
|
|
356
|
+
assert len(desc) == 4
|
|
357
|
+
names = [x[0] for x in out]
|
|
358
|
+
assert names == ['show-ws-3', 'show-ws-2']
|
|
359
|
+
|
|
360
|
+
# All options
|
|
361
|
+
self.cur.execute(
|
|
362
|
+
f'show workspaces in group "B Fusion Testing {self.id}" '
|
|
363
|
+
'like "show-ws%" extended order by name desc limit 2',
|
|
364
|
+
)
|
|
365
|
+
out = list(self.cur)
|
|
366
|
+
desc = self.cur.description
|
|
367
|
+
assert len(desc) == 7
|
|
368
|
+
names = [x[0] for x in out]
|
|
369
|
+
assert names == ['show-ws-3', 'show-ws-2']
|
|
370
|
+
|
|
371
|
+
def test_create_drop_workspace(self):
|
|
372
|
+
mgr = s2.manage_workspaces()
|
|
373
|
+
wg = mgr.workspace_groups[f'A Fusion Testing {self.id}']
|
|
374
|
+
|
|
375
|
+
self.cur.execute(
|
|
376
|
+
f'create workspace foobar-1 in group "A Fusion Testing {self.id}" '
|
|
377
|
+
'with size S-00 wait on active',
|
|
378
|
+
)
|
|
379
|
+
foobar_1 = [x for x in wg.workspaces if x.name == 'foobar-1']
|
|
380
|
+
assert len(foobar_1) == 1
|
|
381
|
+
|
|
382
|
+
self.cur.execute(
|
|
383
|
+
f'create workspace foobar-2 in group "A Fusion Testing {self.id}" '
|
|
384
|
+
'with size S-00 wait on active',
|
|
385
|
+
)
|
|
386
|
+
foobar_2 = [x for x in wg.workspaces if x.name == 'foobar-2']
|
|
387
|
+
assert len(foobar_2) == 1
|
|
388
|
+
|
|
389
|
+
# Drop by name
|
|
390
|
+
self.cur.execute(
|
|
391
|
+
f'drop workspace "foobar-1" in group "A Fusion Testing {self.id}" '
|
|
392
|
+
'wait on terminated',
|
|
393
|
+
)
|
|
394
|
+
foobar_1 = [x for x in wg.workspaces if x.name == 'foobar-1']
|
|
395
|
+
assert len(foobar_1) == 0
|
|
396
|
+
|
|
397
|
+
# Drop by ID
|
|
398
|
+
foobar_2_id = foobar_2[0].id
|
|
399
|
+
self.cur.execute(
|
|
400
|
+
f'drop workspace id {foobar_2_id} in group '
|
|
401
|
+
f'"A Fusion Testing {self.id}" wait on terminated',
|
|
402
|
+
)
|
|
403
|
+
foobar_2 = [x for x in wg.workspaces if x.name == 'foobar-2']
|
|
404
|
+
assert len(foobar_2) == 0
|
|
405
|
+
|
|
406
|
+
# Drop non-existent by ID
|
|
407
|
+
with self.assertRaises(KeyError):
|
|
408
|
+
self.cur.execute(
|
|
409
|
+
f'drop workspace id {foobar_2_id} '
|
|
410
|
+
f'in group "A Fusion Testing {self.id}"',
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# Drop non-existent by ID with IF EXISTS
|
|
414
|
+
self.cur.execute(
|
|
415
|
+
f'drop workspace IF EXISTS id {foobar_2_id} '
|
|
416
|
+
f'in group "A Fusion Testing {self.id}"',
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
def test_create_drop_workspace_group(self):
|
|
420
|
+
mgr = s2.manage_workspaces()
|
|
421
|
+
|
|
422
|
+
reg = [x for x in mgr.regions if x.name.startswith('US')][0]
|
|
423
|
+
wg_name = f'Create WG Test {id(self)}'
|
|
424
|
+
|
|
425
|
+
try:
|
|
426
|
+
self.cur.execute(
|
|
427
|
+
f'create workspace group "{wg_name}" '
|
|
428
|
+
f'in region "{reg.name}"',
|
|
429
|
+
)
|
|
430
|
+
wg = [x for x in mgr.workspace_groups if x.name == wg_name]
|
|
431
|
+
assert len(wg) == 1
|
|
432
|
+
|
|
433
|
+
# Drop it by name
|
|
434
|
+
self.cur.execute(
|
|
435
|
+
f'drop workspace group "{wg_name}" '
|
|
436
|
+
'wait on terminated',
|
|
437
|
+
)
|
|
438
|
+
wg = [x for x in mgr.workspace_groups if x.name == wg_name]
|
|
439
|
+
assert len(wg) == 0
|
|
440
|
+
|
|
441
|
+
# Create it again
|
|
442
|
+
self.cur.execute(
|
|
443
|
+
f'create workspace group "{wg_name}" in region "{reg.name}"',
|
|
444
|
+
)
|
|
445
|
+
wg = [x for x in mgr.workspace_groups if x.name == wg_name]
|
|
446
|
+
assert len(wg) == 1
|
|
447
|
+
|
|
448
|
+
# Drop it by ID
|
|
449
|
+
wg_id = wg[0].id
|
|
450
|
+
self.cur.execute(f'drop workspace group id {wg_id} wait on terminated')
|
|
451
|
+
wg = [x for x in mgr.workspace_groups if x.name == wg_name]
|
|
452
|
+
assert len(wg) == 0
|
|
453
|
+
|
|
454
|
+
# Drop non-existent
|
|
455
|
+
with self.assertRaises(KeyError):
|
|
456
|
+
self.cur.execute(f'drop workspace group id {wg_id}')
|
|
457
|
+
|
|
458
|
+
# Drop non-existent with IF EXISTS
|
|
459
|
+
self.cur.execute(f'drop workspace group if exists id {wg_id}')
|
|
460
|
+
|
|
461
|
+
finally:
|
|
462
|
+
try:
|
|
463
|
+
mgr.workspace_groups[wg_name].terminate(force=True)
|
|
464
|
+
except Exception:
|
|
465
|
+
pass
|
singlestoredb/tests/test_http.py
CHANGED
|
@@ -30,9 +30,9 @@ class TestHTTP(unittest.TestCase):
|
|
|
30
30
|
def setUp(self):
|
|
31
31
|
self.conn = self._connect()
|
|
32
32
|
self.cur = self.conn.cursor()
|
|
33
|
-
if self.params['
|
|
33
|
+
if self.params['driver'] not in ['http', 'https']:
|
|
34
34
|
self.skipTest('Tests must be run using HTTP connection')
|
|
35
|
-
self.driver = self.params['
|
|
35
|
+
self.driver = self.params['driver'] or 'http'
|
|
36
36
|
|
|
37
37
|
def _connect(self):
|
|
38
38
|
params = sc.build_params(host=config.get_option('host'))
|
|
@@ -42,7 +42,7 @@ class TestHTTP(unittest.TestCase):
|
|
|
42
42
|
port=params.get('port'),
|
|
43
43
|
user=params.get('user'),
|
|
44
44
|
password=params.get('password'),
|
|
45
|
-
|
|
45
|
+
driver=params.get('driver'),
|
|
46
46
|
).items() if v is not None
|
|
47
47
|
}
|
|
48
48
|
return http.connect(database=type(self).dbname, **self.params)
|
|
@@ -63,56 +63,62 @@ class TestHTTP(unittest.TestCase):
|
|
|
63
63
|
pass
|
|
64
64
|
|
|
65
65
|
def test_get_exc_type(self):
|
|
66
|
-
exc = http.get_exc_type(0)
|
|
66
|
+
exc = http.connection.get_exc_type(0)
|
|
67
67
|
assert exc is http.InterfaceError, exc
|
|
68
68
|
|
|
69
|
-
exc = http.get_exc_type(2012)
|
|
69
|
+
exc = http.connection.get_exc_type(2012)
|
|
70
70
|
assert exc is http.InterfaceError, exc
|
|
71
71
|
|
|
72
|
-
exc = http.get_exc_type(1230)
|
|
72
|
+
exc = http.connection.get_exc_type(1230)
|
|
73
73
|
assert exc is http.DataError, exc
|
|
74
74
|
|
|
75
|
-
exc = http.get_exc_type(1110)
|
|
75
|
+
exc = http.connection.get_exc_type(1110)
|
|
76
76
|
assert exc is http.ProgrammingError, exc
|
|
77
77
|
|
|
78
|
-
exc = http.get_exc_type(1452)
|
|
78
|
+
exc = http.connection.get_exc_type(1452)
|
|
79
79
|
assert exc is http.IntegrityError, exc
|
|
80
80
|
|
|
81
|
-
exc = http.get_exc_type(9999)
|
|
81
|
+
exc = http.connection.get_exc_type(9999)
|
|
82
82
|
assert exc is http.OperationalError, exc
|
|
83
83
|
|
|
84
|
-
exc = http.get_exc_type(222)
|
|
84
|
+
exc = http.connection.get_exc_type(222)
|
|
85
85
|
assert exc is http.InternalError, exc
|
|
86
86
|
|
|
87
87
|
def test_identity(self):
|
|
88
|
-
out = http.identity(1)
|
|
88
|
+
out = http.connection.identity(1)
|
|
89
89
|
assert out == 1, out
|
|
90
90
|
|
|
91
|
-
out = http.identity('hi')
|
|
91
|
+
out = http.connection.identity('hi')
|
|
92
92
|
assert out == 'hi', out
|
|
93
93
|
|
|
94
94
|
def test_b64decode_converter(self):
|
|
95
95
|
data = base64.b64encode(b'hi there')
|
|
96
96
|
assert type(data) is bytes, type(data)
|
|
97
97
|
|
|
98
|
-
out = http.b64decode_converter(http.identity, None)
|
|
98
|
+
out = http.connection.b64decode_converter(http.connection.identity, None)
|
|
99
99
|
assert out is None, out
|
|
100
100
|
|
|
101
|
-
out = http.b64decode_converter(http.identity, data)
|
|
101
|
+
out = http.connection.b64decode_converter(http.connection.identity, data)
|
|
102
102
|
assert out == b'hi there', out
|
|
103
103
|
|
|
104
|
-
out = http.b64decode_converter(
|
|
104
|
+
out = http.connection.b64decode_converter(
|
|
105
|
+
http.connection.identity,
|
|
106
|
+
str(data, 'utf8'),
|
|
107
|
+
)
|
|
105
108
|
assert out == b'hi there', out
|
|
106
109
|
|
|
107
110
|
def test_executemany(self):
|
|
108
|
-
self.cur.executemany(
|
|
111
|
+
self.cur.executemany(
|
|
112
|
+
'select * from data where id < %(id)s',
|
|
113
|
+
[dict(id='d'), dict(id='e')],
|
|
114
|
+
)
|
|
109
115
|
|
|
110
116
|
assert self.cur.rownumber == 0, self.cur.rownumber
|
|
111
117
|
|
|
112
118
|
# First set
|
|
113
119
|
out = self.cur.fetchall()
|
|
114
120
|
|
|
115
|
-
assert self.cur.rownumber
|
|
121
|
+
assert self.cur.rownumber == 3, self.cur.rownumber
|
|
116
122
|
|
|
117
123
|
desc = self.cur.description
|
|
118
124
|
rowcount = self.cur.rowcount
|
|
@@ -124,7 +130,7 @@ class TestHTTP(unittest.TestCase):
|
|
|
124
130
|
('c', 'cats', 5),
|
|
125
131
|
]), out
|
|
126
132
|
|
|
127
|
-
assert rowcount ==
|
|
133
|
+
assert rowcount == 7, rowcount
|
|
128
134
|
assert lastrowid is None, lastrowid
|
|
129
135
|
assert len(desc) == 3, desc
|
|
130
136
|
assert desc[0][0] == 'id', desc[0][0]
|
|
@@ -161,7 +167,7 @@ class TestHTTP(unittest.TestCase):
|
|
|
161
167
|
assert desc[2][1] == 8, desc[2][1]
|
|
162
168
|
|
|
163
169
|
out = self.cur.nextset()
|
|
164
|
-
assert out is
|
|
170
|
+
assert out is None, out
|
|
165
171
|
|
|
166
172
|
def test_executemany_no_args(self):
|
|
167
173
|
self.cur.executemany('select * from data where id < "d"')
|
|
@@ -190,7 +196,7 @@ class TestHTTP(unittest.TestCase):
|
|
|
190
196
|
assert desc[2][1] == 8, desc[2][1]
|
|
191
197
|
|
|
192
198
|
out = self.cur.nextset()
|
|
193
|
-
assert out is
|
|
199
|
+
assert out is None, out
|
|
194
200
|
|
|
195
201
|
def test_is_connected(self):
|
|
196
202
|
assert self.cur.is_connected() is True
|
|
@@ -201,18 +207,18 @@ class TestHTTP(unittest.TestCase):
|
|
|
201
207
|
self.cur.close()
|
|
202
208
|
assert self.cur.is_connected() is False
|
|
203
209
|
|
|
204
|
-
with self.assertRaises(http.
|
|
210
|
+
with self.assertRaises(http.ProgrammingError):
|
|
205
211
|
self.cur.execute('select 1')
|
|
206
212
|
|
|
207
|
-
with self.assertRaises(http.
|
|
213
|
+
with self.assertRaises(http.ProgrammingError):
|
|
208
214
|
self.cur.executemany('select 1')
|
|
209
215
|
|
|
210
|
-
with self.assertRaises(http.
|
|
216
|
+
with self.assertRaises(http.ProgrammingError):
|
|
211
217
|
self.cur.callproc('get_animal', ['cats'])
|
|
212
218
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
219
|
+
# def test_callproc(self):
|
|
220
|
+
# with self.assertRaises(NotImplementedError):
|
|
221
|
+
# self.cur.callproc('get_animal', ['cats'])
|
|
216
222
|
|
|
217
223
|
def test_iter(self):
|
|
218
224
|
self.cur.execute('select * from data')
|