singlestoredb 0.3.3__py3-none-any.whl → 1.0.3__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 -2
- singlestoredb/alchemy/__init__.py +90 -0
- singlestoredb/auth.py +6 -4
- singlestoredb/config.py +116 -16
- singlestoredb/connection.py +489 -523
- singlestoredb/converters.py +275 -26
- singlestoredb/exceptions.py +30 -4
- 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/__init__.py +0 -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/connection.py +1192 -0
- singlestoredb/management/__init__.py +3 -2
- singlestoredb/management/billing_usage.py +148 -0
- singlestoredb/management/cluster.py +19 -14
- singlestoredb/management/manager.py +100 -40
- singlestoredb/management/organization.py +188 -0
- singlestoredb/management/region.py +6 -8
- singlestoredb/management/utils.py +253 -4
- singlestoredb/management/workspace.py +1153 -35
- singlestoredb/mysql/__init__.py +177 -0
- singlestoredb/mysql/_auth.py +298 -0
- singlestoredb/mysql/charset.py +214 -0
- singlestoredb/mysql/connection.py +1814 -0
- singlestoredb/mysql/constants/CLIENT.py +38 -0
- singlestoredb/mysql/constants/COMMAND.py +32 -0
- singlestoredb/mysql/constants/CR.py +78 -0
- singlestoredb/mysql/constants/ER.py +474 -0
- singlestoredb/mysql/constants/FIELD_TYPE.py +32 -0
- singlestoredb/mysql/constants/FLAG.py +15 -0
- singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
- singlestoredb/mysql/constants/__init__.py +0 -0
- singlestoredb/mysql/converters.py +271 -0
- singlestoredb/mysql/cursors.py +713 -0
- singlestoredb/mysql/err.py +92 -0
- singlestoredb/mysql/optionfile.py +20 -0
- singlestoredb/mysql/protocol.py +388 -0
- singlestoredb/mysql/tests/__init__.py +19 -0
- singlestoredb/mysql/tests/base.py +126 -0
- singlestoredb/mysql/tests/conftest.py +37 -0
- singlestoredb/mysql/tests/test_DictCursor.py +132 -0
- singlestoredb/mysql/tests/test_SSCursor.py +141 -0
- singlestoredb/mysql/tests/test_basic.py +452 -0
- singlestoredb/mysql/tests/test_connection.py +851 -0
- singlestoredb/mysql/tests/test_converters.py +58 -0
- singlestoredb/mysql/tests/test_cursor.py +141 -0
- singlestoredb/mysql/tests/test_err.py +16 -0
- singlestoredb/mysql/tests/test_issues.py +514 -0
- singlestoredb/mysql/tests/test_load_local.py +75 -0
- singlestoredb/mysql/tests/test_nextset.py +88 -0
- singlestoredb/mysql/tests/test_optionfile.py +27 -0
- singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
- singlestoredb/mysql/times.py +23 -0
- 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 -117
- singlestoredb/tests/test_config.py +13 -15
- singlestoredb/tests/test_connection.py +241 -289
- singlestoredb/tests/test_dbapi.py +27 -0
- singlestoredb/tests/test_exceptions.py +0 -2
- 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 -28
- singlestoredb/tests/test_management.py +588 -10
- singlestoredb/tests/test_plugin.py +33 -0
- singlestoredb/tests/test_results.py +11 -14
- singlestoredb/tests/test_types.py +0 -2
- singlestoredb/tests/test_udf.py +687 -0
- singlestoredb/tests/test_xdict.py +0 -2
- singlestoredb/tests/utils.py +3 -4
- singlestoredb/types.py +4 -5
- singlestoredb/utils/config.py +71 -12
- singlestoredb/utils/convert_rows.py +0 -2
- singlestoredb/utils/debug.py +13 -0
- singlestoredb/utils/mogrify.py +151 -0
- singlestoredb/utils/results.py +4 -3
- singlestoredb/utils/xdict.py +12 -12
- singlestoredb-1.0.3.dist-info/METADATA +139 -0
- singlestoredb-1.0.3.dist-info/RECORD +112 -0
- {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.dist-info}/WHEEL +1 -1
- singlestoredb-1.0.3.dist-info/entry_points.txt +2 -0
- singlestoredb/drivers/__init__.py +0 -46
- singlestoredb/drivers/base.py +0 -200
- singlestoredb/drivers/cymysql.py +0 -40
- singlestoredb/drivers/http.py +0 -49
- singlestoredb/drivers/mariadb.py +0 -42
- singlestoredb/drivers/mysqlconnector.py +0 -51
- singlestoredb/drivers/mysqldb.py +0 -62
- singlestoredb/drivers/pymysql.py +0 -39
- singlestoredb/drivers/pyodbc.py +0 -67
- singlestoredb/http.py +0 -794
- singlestoredb-0.3.3.dist-info/METADATA +0 -105
- singlestoredb-0.3.3.dist-info/RECORD +0 -46
- {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.dist-info}/LICENSE +0 -0
- {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.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
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
# type: ignore
|
|
3
3
|
"""SingleStoreDB HTTP connection testing."""
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
4
|
import base64
|
|
7
5
|
import os
|
|
8
6
|
import unittest
|
|
@@ -32,9 +30,9 @@ class TestHTTP(unittest.TestCase):
|
|
|
32
30
|
def setUp(self):
|
|
33
31
|
self.conn = self._connect()
|
|
34
32
|
self.cur = self.conn.cursor()
|
|
35
|
-
if self.params['
|
|
33
|
+
if self.params['driver'] not in ['http', 'https']:
|
|
36
34
|
self.skipTest('Tests must be run using HTTP connection')
|
|
37
|
-
self.driver = self.params['
|
|
35
|
+
self.driver = self.params['driver'] or 'http'
|
|
38
36
|
|
|
39
37
|
def _connect(self):
|
|
40
38
|
params = sc.build_params(host=config.get_option('host'))
|
|
@@ -44,7 +42,7 @@ class TestHTTP(unittest.TestCase):
|
|
|
44
42
|
port=params.get('port'),
|
|
45
43
|
user=params.get('user'),
|
|
46
44
|
password=params.get('password'),
|
|
47
|
-
|
|
45
|
+
driver=params.get('driver'),
|
|
48
46
|
).items() if v is not None
|
|
49
47
|
}
|
|
50
48
|
return http.connect(database=type(self).dbname, **self.params)
|
|
@@ -65,56 +63,62 @@ class TestHTTP(unittest.TestCase):
|
|
|
65
63
|
pass
|
|
66
64
|
|
|
67
65
|
def test_get_exc_type(self):
|
|
68
|
-
exc = http.get_exc_type(0)
|
|
66
|
+
exc = http.connection.get_exc_type(0)
|
|
69
67
|
assert exc is http.InterfaceError, exc
|
|
70
68
|
|
|
71
|
-
exc = http.get_exc_type(2012)
|
|
69
|
+
exc = http.connection.get_exc_type(2012)
|
|
72
70
|
assert exc is http.InterfaceError, exc
|
|
73
71
|
|
|
74
|
-
exc = http.get_exc_type(1230)
|
|
72
|
+
exc = http.connection.get_exc_type(1230)
|
|
75
73
|
assert exc is http.DataError, exc
|
|
76
74
|
|
|
77
|
-
exc = http.get_exc_type(1110)
|
|
75
|
+
exc = http.connection.get_exc_type(1110)
|
|
78
76
|
assert exc is http.ProgrammingError, exc
|
|
79
77
|
|
|
80
|
-
exc = http.get_exc_type(1452)
|
|
78
|
+
exc = http.connection.get_exc_type(1452)
|
|
81
79
|
assert exc is http.IntegrityError, exc
|
|
82
80
|
|
|
83
|
-
exc = http.get_exc_type(9999)
|
|
81
|
+
exc = http.connection.get_exc_type(9999)
|
|
84
82
|
assert exc is http.OperationalError, exc
|
|
85
83
|
|
|
86
|
-
exc = http.get_exc_type(222)
|
|
84
|
+
exc = http.connection.get_exc_type(222)
|
|
87
85
|
assert exc is http.InternalError, exc
|
|
88
86
|
|
|
89
87
|
def test_identity(self):
|
|
90
|
-
out = http.identity(1)
|
|
88
|
+
out = http.connection.identity(1)
|
|
91
89
|
assert out == 1, out
|
|
92
90
|
|
|
93
|
-
out = http.identity('hi')
|
|
91
|
+
out = http.connection.identity('hi')
|
|
94
92
|
assert out == 'hi', out
|
|
95
93
|
|
|
96
94
|
def test_b64decode_converter(self):
|
|
97
95
|
data = base64.b64encode(b'hi there')
|
|
98
96
|
assert type(data) is bytes, type(data)
|
|
99
97
|
|
|
100
|
-
out = http.b64decode_converter(http.identity, None)
|
|
98
|
+
out = http.connection.b64decode_converter(http.connection.identity, None)
|
|
101
99
|
assert out is None, out
|
|
102
100
|
|
|
103
|
-
out = http.b64decode_converter(http.identity, data)
|
|
101
|
+
out = http.connection.b64decode_converter(http.connection.identity, data)
|
|
104
102
|
assert out == b'hi there', out
|
|
105
103
|
|
|
106
|
-
out = http.b64decode_converter(
|
|
104
|
+
out = http.connection.b64decode_converter(
|
|
105
|
+
http.connection.identity,
|
|
106
|
+
str(data, 'utf8'),
|
|
107
|
+
)
|
|
107
108
|
assert out == b'hi there', out
|
|
108
109
|
|
|
109
110
|
def test_executemany(self):
|
|
110
|
-
self.cur.executemany(
|
|
111
|
+
self.cur.executemany(
|
|
112
|
+
'select * from data where id < %(id)s',
|
|
113
|
+
[dict(id='d'), dict(id='e')],
|
|
114
|
+
)
|
|
111
115
|
|
|
112
116
|
assert self.cur.rownumber == 0, self.cur.rownumber
|
|
113
117
|
|
|
114
118
|
# First set
|
|
115
119
|
out = self.cur.fetchall()
|
|
116
120
|
|
|
117
|
-
assert self.cur.rownumber
|
|
121
|
+
assert self.cur.rownumber == 3, self.cur.rownumber
|
|
118
122
|
|
|
119
123
|
desc = self.cur.description
|
|
120
124
|
rowcount = self.cur.rowcount
|
|
@@ -126,7 +130,7 @@ class TestHTTP(unittest.TestCase):
|
|
|
126
130
|
('c', 'cats', 5),
|
|
127
131
|
]), out
|
|
128
132
|
|
|
129
|
-
assert rowcount ==
|
|
133
|
+
assert rowcount == 7, rowcount
|
|
130
134
|
assert lastrowid is None, lastrowid
|
|
131
135
|
assert len(desc) == 3, desc
|
|
132
136
|
assert desc[0][0] == 'id', desc[0][0]
|
|
@@ -163,7 +167,7 @@ class TestHTTP(unittest.TestCase):
|
|
|
163
167
|
assert desc[2][1] == 8, desc[2][1]
|
|
164
168
|
|
|
165
169
|
out = self.cur.nextset()
|
|
166
|
-
assert out is
|
|
170
|
+
assert out is None, out
|
|
167
171
|
|
|
168
172
|
def test_executemany_no_args(self):
|
|
169
173
|
self.cur.executemany('select * from data where id < "d"')
|
|
@@ -192,7 +196,7 @@ class TestHTTP(unittest.TestCase):
|
|
|
192
196
|
assert desc[2][1] == 8, desc[2][1]
|
|
193
197
|
|
|
194
198
|
out = self.cur.nextset()
|
|
195
|
-
assert out is
|
|
199
|
+
assert out is None, out
|
|
196
200
|
|
|
197
201
|
def test_is_connected(self):
|
|
198
202
|
assert self.cur.is_connected() is True
|
|
@@ -203,18 +207,18 @@ class TestHTTP(unittest.TestCase):
|
|
|
203
207
|
self.cur.close()
|
|
204
208
|
assert self.cur.is_connected() is False
|
|
205
209
|
|
|
206
|
-
with self.assertRaises(http.
|
|
210
|
+
with self.assertRaises(http.ProgrammingError):
|
|
207
211
|
self.cur.execute('select 1')
|
|
208
212
|
|
|
209
|
-
with self.assertRaises(http.
|
|
213
|
+
with self.assertRaises(http.ProgrammingError):
|
|
210
214
|
self.cur.executemany('select 1')
|
|
211
215
|
|
|
212
|
-
with self.assertRaises(http.
|
|
216
|
+
with self.assertRaises(http.ProgrammingError):
|
|
213
217
|
self.cur.callproc('get_animal', ['cats'])
|
|
214
218
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
219
|
+
# def test_callproc(self):
|
|
220
|
+
# with self.assertRaises(NotImplementedError):
|
|
221
|
+
# self.cur.callproc('get_animal', ['cats'])
|
|
218
222
|
|
|
219
223
|
def test_iter(self):
|
|
220
224
|
self.cur.execute('select * from data')
|