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.
- singlestoredb/__init__.py +75 -0
- singlestoredb/ai/__init__.py +2 -0
- singlestoredb/ai/chat.py +139 -0
- singlestoredb/ai/embeddings.py +128 -0
- singlestoredb/alchemy/__init__.py +90 -0
- singlestoredb/apps/__init__.py +3 -0
- singlestoredb/apps/_cloud_functions.py +90 -0
- singlestoredb/apps/_config.py +72 -0
- singlestoredb/apps/_connection_info.py +18 -0
- singlestoredb/apps/_dashboards.py +47 -0
- singlestoredb/apps/_process.py +32 -0
- singlestoredb/apps/_python_udfs.py +100 -0
- singlestoredb/apps/_stdout_supress.py +30 -0
- singlestoredb/apps/_uvicorn_util.py +36 -0
- singlestoredb/auth.py +245 -0
- singlestoredb/config.py +484 -0
- singlestoredb/connection.py +1487 -0
- singlestoredb/converters.py +950 -0
- singlestoredb/docstring/__init__.py +33 -0
- singlestoredb/docstring/attrdoc.py +126 -0
- singlestoredb/docstring/common.py +230 -0
- singlestoredb/docstring/epydoc.py +267 -0
- singlestoredb/docstring/google.py +412 -0
- singlestoredb/docstring/numpydoc.py +562 -0
- singlestoredb/docstring/parser.py +100 -0
- singlestoredb/docstring/py.typed +1 -0
- singlestoredb/docstring/rest.py +256 -0
- singlestoredb/docstring/tests/__init__.py +1 -0
- singlestoredb/docstring/tests/_pydoctor.py +21 -0
- singlestoredb/docstring/tests/test_epydoc.py +729 -0
- singlestoredb/docstring/tests/test_google.py +1007 -0
- singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
- singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
- singlestoredb/docstring/tests/test_parser.py +248 -0
- singlestoredb/docstring/tests/test_rest.py +547 -0
- singlestoredb/docstring/tests/test_util.py +70 -0
- singlestoredb/docstring/util.py +141 -0
- singlestoredb/exceptions.py +120 -0
- singlestoredb/functions/__init__.py +16 -0
- singlestoredb/functions/decorator.py +201 -0
- singlestoredb/functions/dtypes.py +1793 -0
- singlestoredb/functions/ext/__init__.py +1 -0
- singlestoredb/functions/ext/arrow.py +375 -0
- singlestoredb/functions/ext/asgi.py +2133 -0
- singlestoredb/functions/ext/json.py +420 -0
- singlestoredb/functions/ext/mmap.py +413 -0
- singlestoredb/functions/ext/rowdat_1.py +724 -0
- singlestoredb/functions/ext/timer.py +89 -0
- singlestoredb/functions/ext/utils.py +218 -0
- singlestoredb/functions/signature.py +1578 -0
- singlestoredb/functions/typing/__init__.py +41 -0
- singlestoredb/functions/typing/numpy.py +20 -0
- singlestoredb/functions/typing/pandas.py +2 -0
- singlestoredb/functions/typing/polars.py +2 -0
- singlestoredb/functions/typing/pyarrow.py +2 -0
- singlestoredb/functions/utils.py +421 -0
- singlestoredb/fusion/__init__.py +11 -0
- singlestoredb/fusion/graphql.py +213 -0
- singlestoredb/fusion/handler.py +916 -0
- singlestoredb/fusion/handlers/__init__.py +0 -0
- singlestoredb/fusion/handlers/export.py +525 -0
- singlestoredb/fusion/handlers/files.py +690 -0
- singlestoredb/fusion/handlers/job.py +660 -0
- singlestoredb/fusion/handlers/models.py +250 -0
- singlestoredb/fusion/handlers/stage.py +502 -0
- singlestoredb/fusion/handlers/utils.py +324 -0
- singlestoredb/fusion/handlers/workspace.py +956 -0
- singlestoredb/fusion/registry.py +249 -0
- singlestoredb/fusion/result.py +399 -0
- singlestoredb/http/__init__.py +27 -0
- singlestoredb/http/connection.py +1267 -0
- singlestoredb/magics/__init__.py +34 -0
- singlestoredb/magics/run_personal.py +137 -0
- singlestoredb/magics/run_shared.py +134 -0
- singlestoredb/management/__init__.py +9 -0
- singlestoredb/management/billing_usage.py +148 -0
- singlestoredb/management/cluster.py +462 -0
- singlestoredb/management/export.py +295 -0
- singlestoredb/management/files.py +1102 -0
- singlestoredb/management/inference_api.py +105 -0
- singlestoredb/management/job.py +887 -0
- singlestoredb/management/manager.py +373 -0
- singlestoredb/management/organization.py +226 -0
- singlestoredb/management/region.py +169 -0
- singlestoredb/management/utils.py +423 -0
- singlestoredb/management/workspace.py +1927 -0
- singlestoredb/mysql/__init__.py +177 -0
- singlestoredb/mysql/_auth.py +298 -0
- singlestoredb/mysql/charset.py +214 -0
- singlestoredb/mysql/connection.py +2032 -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/EXTENDED_TYPE.py +3 -0
- singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
- singlestoredb/mysql/constants/FLAG.py +15 -0
- singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
- singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
- singlestoredb/mysql/constants/__init__.py +0 -0
- singlestoredb/mysql/converters.py +271 -0
- singlestoredb/mysql/cursors.py +896 -0
- singlestoredb/mysql/err.py +92 -0
- singlestoredb/mysql/optionfile.py +20 -0
- singlestoredb/mysql/protocol.py +450 -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/notebook/__init__.py +16 -0
- singlestoredb/notebook/_objects.py +213 -0
- singlestoredb/notebook/_portal.py +352 -0
- singlestoredb/py.typed +0 -0
- singlestoredb/pytest.py +352 -0
- singlestoredb/server/__init__.py +0 -0
- singlestoredb/server/docker.py +452 -0
- singlestoredb/server/free_tier.py +267 -0
- singlestoredb/tests/__init__.py +0 -0
- singlestoredb/tests/alltypes.sql +307 -0
- singlestoredb/tests/alltypes_no_nulls.sql +208 -0
- singlestoredb/tests/empty.sql +0 -0
- singlestoredb/tests/ext_funcs/__init__.py +702 -0
- singlestoredb/tests/local_infile.csv +3 -0
- singlestoredb/tests/test.ipynb +18 -0
- singlestoredb/tests/test.sql +680 -0
- singlestoredb/tests/test2.ipynb +18 -0
- singlestoredb/tests/test2.sql +1 -0
- singlestoredb/tests/test_basics.py +1332 -0
- singlestoredb/tests/test_config.py +318 -0
- singlestoredb/tests/test_connection.py +3103 -0
- singlestoredb/tests/test_dbapi.py +27 -0
- singlestoredb/tests/test_exceptions.py +45 -0
- singlestoredb/tests/test_ext_func.py +1472 -0
- singlestoredb/tests/test_ext_func_data.py +1101 -0
- singlestoredb/tests/test_fusion.py +1527 -0
- singlestoredb/tests/test_http.py +288 -0
- singlestoredb/tests/test_management.py +1599 -0
- singlestoredb/tests/test_plugin.py +33 -0
- singlestoredb/tests/test_results.py +171 -0
- singlestoredb/tests/test_types.py +132 -0
- singlestoredb/tests/test_udf.py +737 -0
- singlestoredb/tests/test_udf_returns.py +459 -0
- singlestoredb/tests/test_vectorstore.py +51 -0
- singlestoredb/tests/test_xdict.py +333 -0
- singlestoredb/tests/utils.py +141 -0
- singlestoredb/types.py +373 -0
- singlestoredb/utils/__init__.py +0 -0
- singlestoredb/utils/config.py +950 -0
- singlestoredb/utils/convert_rows.py +69 -0
- singlestoredb/utils/debug.py +13 -0
- singlestoredb/utils/dtypes.py +205 -0
- singlestoredb/utils/events.py +65 -0
- singlestoredb/utils/mogrify.py +151 -0
- singlestoredb/utils/results.py +585 -0
- singlestoredb/utils/xdict.py +425 -0
- singlestoredb/vectorstore.py +192 -0
- singlestoredb/warnings.py +5 -0
- singlestoredb-1.16.1.dist-info/METADATA +165 -0
- singlestoredb-1.16.1.dist-info/RECORD +183 -0
- singlestoredb-1.16.1.dist-info/WHEEL +5 -0
- singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
- singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
- singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
- sqlx/__init__.py +4 -0
- sqlx/magic.py +113 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
import singlestoredb.mysql as sv
|
|
5
|
+
from . import capabilities
|
|
6
|
+
from singlestoredb.mysql.tests import base
|
|
7
|
+
|
|
8
|
+
warnings.filterwarnings('error')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class test_MySQLdb(capabilities.DatabaseTest):
|
|
12
|
+
|
|
13
|
+
db_module = sv
|
|
14
|
+
connect_args = ()
|
|
15
|
+
connect_kwargs = base.PyMySQLTestCase.databases[0].copy()
|
|
16
|
+
connect_kwargs.update(
|
|
17
|
+
dict(
|
|
18
|
+
read_default_file='~/.my.cnf',
|
|
19
|
+
use_unicode=True,
|
|
20
|
+
binary_prefix=True,
|
|
21
|
+
charset='utf8mb4',
|
|
22
|
+
sql_mode='ANSI,STRICT_TRANS_TABLES,TRADITIONAL',
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
leak_test = False
|
|
27
|
+
|
|
28
|
+
def quote_identifier(self, ident):
|
|
29
|
+
return '`%s`' % ident
|
|
30
|
+
|
|
31
|
+
def test_TIME(self):
|
|
32
|
+
from datetime import timedelta
|
|
33
|
+
|
|
34
|
+
def generator(row, col):
|
|
35
|
+
return timedelta(0, row * 8000)
|
|
36
|
+
|
|
37
|
+
self.check_data_integrity(('col1 TIME',), generator)
|
|
38
|
+
|
|
39
|
+
def test_TINYINT(self):
|
|
40
|
+
# Number data
|
|
41
|
+
def generator(row, col):
|
|
42
|
+
v = (row * row) % 256
|
|
43
|
+
if v > 127:
|
|
44
|
+
v = v - 256
|
|
45
|
+
return v
|
|
46
|
+
|
|
47
|
+
self.check_data_integrity(('col1 TINYINT',), generator)
|
|
48
|
+
|
|
49
|
+
def test_stored_procedures(self):
|
|
50
|
+
db = self.connection
|
|
51
|
+
c = self.cursor
|
|
52
|
+
try:
|
|
53
|
+
self.create_table(('pos INT', 'tree CHAR(20)'))
|
|
54
|
+
c.executemany(
|
|
55
|
+
'INSERT INTO %s (pos,tree) VALUES (%%s,%%s)' % self.table,
|
|
56
|
+
list(enumerate('ash birch cedar larch pine'.split())),
|
|
57
|
+
)
|
|
58
|
+
db.commit()
|
|
59
|
+
|
|
60
|
+
c.execute(
|
|
61
|
+
"""
|
|
62
|
+
CREATE PROCEDURE test_sp(t VARCHAR(255)) AS
|
|
63
|
+
BEGIN
|
|
64
|
+
ECHO SELECT pos FROM %s WHERE tree = t;
|
|
65
|
+
END
|
|
66
|
+
"""
|
|
67
|
+
% self.table,
|
|
68
|
+
)
|
|
69
|
+
db.commit()
|
|
70
|
+
|
|
71
|
+
c.callproc('test_sp', ('larch',))
|
|
72
|
+
rows = c.fetchall()
|
|
73
|
+
self.assertEqual(len(rows), 1)
|
|
74
|
+
self.assertEqual(rows[0][0], 3)
|
|
75
|
+
c.nextset()
|
|
76
|
+
finally:
|
|
77
|
+
c.execute('DROP PROCEDURE IF EXISTS test_sp')
|
|
78
|
+
c.execute('drop table %s' % (self.table))
|
|
79
|
+
|
|
80
|
+
def test_small_CHAR(self):
|
|
81
|
+
# Character data
|
|
82
|
+
def generator(row, col):
|
|
83
|
+
i = ((row + 1) * (col + 1) + 62) % 256
|
|
84
|
+
if i == 62:
|
|
85
|
+
return ''
|
|
86
|
+
if i == 63:
|
|
87
|
+
return None
|
|
88
|
+
return chr(i)
|
|
89
|
+
|
|
90
|
+
self.check_data_integrity(('col1 char(1)', 'col2 char(1)'), generator)
|
|
91
|
+
|
|
92
|
+
def test_bug_2671682(self):
|
|
93
|
+
from singlestoredb.mysql.constants import ER
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
self.cursor.execute('describe some_non_existent_table')
|
|
97
|
+
except self.connection.ProgrammingError as msg:
|
|
98
|
+
self.assertEqual(msg.args[0], ER.NO_SUCH_TABLE)
|
|
99
|
+
|
|
100
|
+
def test_ping(self):
|
|
101
|
+
self.connection.ping()
|
|
102
|
+
|
|
103
|
+
def test_literal_int(self):
|
|
104
|
+
self.assertTrue('2' == self.connection.literal(2))
|
|
105
|
+
|
|
106
|
+
def test_literal_float(self):
|
|
107
|
+
self.assertEqual('3.1415e0', self.connection.literal(3.1415))
|
|
108
|
+
|
|
109
|
+
def test_literal_string(self):
|
|
110
|
+
self.assertTrue("'foo'" == self.connection.literal('foo'))
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import singlestoredb.mysql as sv
|
|
3
|
+
from . import dbapi20
|
|
4
|
+
from singlestoredb.mysql.tests import base
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class test_MySQLdb(dbapi20.DatabaseAPI20Test):
|
|
8
|
+
|
|
9
|
+
driver = sv
|
|
10
|
+
connect_args = ()
|
|
11
|
+
connect_kw_args = base.PyMySQLTestCase.databases[0].copy()
|
|
12
|
+
connect_kw_args.update(
|
|
13
|
+
dict(
|
|
14
|
+
read_default_file='~/.my.cnf',
|
|
15
|
+
charset='utf8',
|
|
16
|
+
sql_mode='ANSI,STRICT_TRANS_TABLES,TRADITIONAL',
|
|
17
|
+
),
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def test_setoutputsize(self):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
def test_setoutputsize_basic(self):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
# The tests on fetchone and fetchall and rowcount bogusly
|
|
27
|
+
# test for an exception if the statement cannot return a
|
|
28
|
+
# result set. MySQL always returns a result set; it's just that
|
|
29
|
+
# some things return empty result sets.
|
|
30
|
+
|
|
31
|
+
def test_fetchall(self):
|
|
32
|
+
con = self._connect()
|
|
33
|
+
try:
|
|
34
|
+
cur = con.cursor()
|
|
35
|
+
# cursor.fetchall should raise an Error if called
|
|
36
|
+
# without executing a query that may return rows (such
|
|
37
|
+
# as a select)
|
|
38
|
+
self.assertRaises(self.driver.Error, cur.fetchall)
|
|
39
|
+
|
|
40
|
+
self.executeDDL1(cur)
|
|
41
|
+
for sql in self._populate():
|
|
42
|
+
cur.execute(sql)
|
|
43
|
+
|
|
44
|
+
# cursor.fetchall should raise an Error if called
|
|
45
|
+
# after executing a a statement that cannot return rows
|
|
46
|
+
# self.assertRaises(self.driver.Error,cur.fetchall)
|
|
47
|
+
|
|
48
|
+
cur.execute('select name from %sbooze' % self.table_prefix)
|
|
49
|
+
rows = cur.fetchall()
|
|
50
|
+
self.assertTrue(cur.rowcount in (-1, len(self.samples)))
|
|
51
|
+
self.assertEqual(
|
|
52
|
+
len(rows),
|
|
53
|
+
len(self.samples),
|
|
54
|
+
'cursor.fetchall did not retrieve all rows',
|
|
55
|
+
)
|
|
56
|
+
rows = [r[0] for r in rows]
|
|
57
|
+
rows.sort()
|
|
58
|
+
for i in range(0, len(self.samples)):
|
|
59
|
+
self.assertEqual(
|
|
60
|
+
rows[i], self.samples[i], 'cursor.fetchall retrieved incorrect rows',
|
|
61
|
+
)
|
|
62
|
+
rows = cur.fetchall()
|
|
63
|
+
self.assertEqual(
|
|
64
|
+
len(rows),
|
|
65
|
+
0,
|
|
66
|
+
'cursor.fetchall should return an empty list if called '
|
|
67
|
+
'after the whole result set has been fetched',
|
|
68
|
+
)
|
|
69
|
+
self.assertTrue(cur.rowcount in (-1, len(self.samples)))
|
|
70
|
+
|
|
71
|
+
self.executeDDL2(cur)
|
|
72
|
+
cur.execute('select name from %sbarflys' % self.table_prefix)
|
|
73
|
+
rows = cur.fetchall()
|
|
74
|
+
self.assertTrue(cur.rowcount in (-1, 0))
|
|
75
|
+
self.assertEqual(
|
|
76
|
+
len(rows),
|
|
77
|
+
0,
|
|
78
|
+
'cursor.fetchall should return an empty list if '
|
|
79
|
+
'a select query returns no rows',
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
finally:
|
|
83
|
+
con.close()
|
|
84
|
+
|
|
85
|
+
def test_fetchone(self):
|
|
86
|
+
con = self._connect()
|
|
87
|
+
try:
|
|
88
|
+
cur = con.cursor()
|
|
89
|
+
|
|
90
|
+
# cursor.fetchone should raise an Error if called before
|
|
91
|
+
# executing a select-type query
|
|
92
|
+
self.assertRaises(self.driver.Error, cur.fetchone)
|
|
93
|
+
|
|
94
|
+
# cursor.fetchone should raise an Error if called after
|
|
95
|
+
# executing a query that cannnot return rows
|
|
96
|
+
self.executeDDL1(cur)
|
|
97
|
+
# self.assertRaises(self.driver.Error,cur.fetchone)
|
|
98
|
+
|
|
99
|
+
cur.execute('select name from %sbooze' % self.table_prefix)
|
|
100
|
+
self.assertEqual(
|
|
101
|
+
cur.fetchone(),
|
|
102
|
+
None,
|
|
103
|
+
'cursor.fetchone should return None if a query retrieves ' 'no rows',
|
|
104
|
+
)
|
|
105
|
+
self.assertTrue(cur.rowcount in (-1, 0))
|
|
106
|
+
|
|
107
|
+
# cursor.fetchone should raise an Error if called after
|
|
108
|
+
# executing a query that cannnot return rows
|
|
109
|
+
cur.execute(
|
|
110
|
+
"insert into %sbooze values ('Victoria Bitter')" % (self.table_prefix),
|
|
111
|
+
)
|
|
112
|
+
# self.assertRaises(self.driver.Error,cur.fetchone)
|
|
113
|
+
|
|
114
|
+
cur.execute('select name from %sbooze' % self.table_prefix)
|
|
115
|
+
r = cur.fetchone()
|
|
116
|
+
self.assertEqual(
|
|
117
|
+
len(r), 1, 'cursor.fetchone should have retrieved a single row',
|
|
118
|
+
)
|
|
119
|
+
self.assertEqual(
|
|
120
|
+
r[0], 'Victoria Bitter', 'cursor.fetchone retrieved incorrect data',
|
|
121
|
+
)
|
|
122
|
+
# self.assertEqual(cur.fetchone(), None,
|
|
123
|
+
# 'cursor.fetchone should return None if '
|
|
124
|
+
# 'no more rows available'
|
|
125
|
+
# )
|
|
126
|
+
self.assertTrue(cur.rowcount in (-1, 1))
|
|
127
|
+
finally:
|
|
128
|
+
con.close()
|
|
129
|
+
|
|
130
|
+
# Same complaint as for fetchall and fetchone
|
|
131
|
+
def test_rowcount(self):
|
|
132
|
+
con = self._connect()
|
|
133
|
+
try:
|
|
134
|
+
cur = con.cursor()
|
|
135
|
+
self.executeDDL1(cur)
|
|
136
|
+
# self.assertEqual(cur.rowcount,-1,
|
|
137
|
+
# 'cursor.rowcount should be -1 after executing no-result '
|
|
138
|
+
# 'statements'
|
|
139
|
+
# )
|
|
140
|
+
cur.execute(
|
|
141
|
+
"insert into %sbooze values ('Victoria Bitter')" % (self.table_prefix),
|
|
142
|
+
)
|
|
143
|
+
# self.assertTrue(cur.rowcount in (-1,1),
|
|
144
|
+
# 'cursor.rowcount should == number or rows inserted, or '
|
|
145
|
+
# 'set to -1 after executing an insert statement'
|
|
146
|
+
# )
|
|
147
|
+
cur.execute('select name from %sbooze' % self.table_prefix)
|
|
148
|
+
self.assertTrue(
|
|
149
|
+
cur.rowcount in (-1, 1),
|
|
150
|
+
'cursor.rowcount should == number of rows returned, or '
|
|
151
|
+
'set to -1 after executing a select statement',
|
|
152
|
+
)
|
|
153
|
+
cur.fetchall()
|
|
154
|
+
self.executeDDL2(cur)
|
|
155
|
+
# self.assertEqual(cur.rowcount, -1,
|
|
156
|
+
# 'cursor.rowcount not being reset to -1 after executing '
|
|
157
|
+
# 'no-result statements'
|
|
158
|
+
# )
|
|
159
|
+
finally:
|
|
160
|
+
con.close()
|
|
161
|
+
|
|
162
|
+
def test_callproc(self):
|
|
163
|
+
pass # performed in test_MySQL_capabilities
|
|
164
|
+
|
|
165
|
+
def help_nextset_setUp(self, cur):
|
|
166
|
+
"""Should create a procedure called deleteme
|
|
167
|
+
that returns two result sets, first the
|
|
168
|
+
number of rows in booze then "name from booze"
|
|
169
|
+
"""
|
|
170
|
+
sql = """
|
|
171
|
+
create procedure deleteme() as
|
|
172
|
+
begin
|
|
173
|
+
echo select count(*) from %(tp)sbooze;
|
|
174
|
+
echo select name from %(tp)sbooze;
|
|
175
|
+
end
|
|
176
|
+
""" % dict(
|
|
177
|
+
tp=self.table_prefix,
|
|
178
|
+
)
|
|
179
|
+
cur.execute(sql)
|
|
180
|
+
|
|
181
|
+
def help_nextset_tearDown(self, cur):
|
|
182
|
+
# If cleaning up is needed after nextSetTest
|
|
183
|
+
cur.execute('drop procedure deleteme')
|
|
184
|
+
|
|
185
|
+
def test_nextset(self):
|
|
186
|
+
# from warnings import warn
|
|
187
|
+
|
|
188
|
+
con = self._connect()
|
|
189
|
+
try:
|
|
190
|
+
cur = con.cursor()
|
|
191
|
+
if not hasattr(cur, 'nextset'):
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
self.executeDDL1(cur)
|
|
196
|
+
sql = self._populate()
|
|
197
|
+
for sql in self._populate():
|
|
198
|
+
cur.execute(sql)
|
|
199
|
+
|
|
200
|
+
self.help_nextset_setUp(cur)
|
|
201
|
+
|
|
202
|
+
cur.callproc('deleteme')
|
|
203
|
+
numberofrows = cur.fetchone()
|
|
204
|
+
assert numberofrows[0] == len(self.samples)
|
|
205
|
+
if cur._result.unbuffered_active:
|
|
206
|
+
assert len(cur.fetchall()) == 0
|
|
207
|
+
assert cur.nextset()
|
|
208
|
+
names = cur.fetchall()
|
|
209
|
+
assert len(names) == len(self.samples)
|
|
210
|
+
s = cur.nextset()
|
|
211
|
+
if s:
|
|
212
|
+
empty = cur.fetchall()
|
|
213
|
+
self.assertEqual(
|
|
214
|
+
len(empty), 0, 'non-empty result set after other result sets',
|
|
215
|
+
)
|
|
216
|
+
# warn("Incompatibility: MySQL returns an empty result set "
|
|
217
|
+
# "for the CALL itself",
|
|
218
|
+
# Warning)
|
|
219
|
+
# assert s == None,'No more return sets, should return None'
|
|
220
|
+
finally:
|
|
221
|
+
self.help_nextset_tearDown(cur)
|
|
222
|
+
|
|
223
|
+
finally:
|
|
224
|
+
con.close()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
import singlestoredb.mysql as sv
|
|
5
|
+
from singlestoredb.mysql.constants import FIELD_TYPE
|
|
6
|
+
from singlestoredb.mysql.tests import base
|
|
7
|
+
|
|
8
|
+
_mysql = sv
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestDBAPISet(unittest.TestCase):
|
|
12
|
+
|
|
13
|
+
def test_set_equality(self):
|
|
14
|
+
self.assertTrue(sv.STRING == sv.STRING)
|
|
15
|
+
|
|
16
|
+
def test_set_inequality(self):
|
|
17
|
+
self.assertTrue(sv.STRING != sv.NUMBER)
|
|
18
|
+
|
|
19
|
+
def test_set_equality_membership(self):
|
|
20
|
+
self.assertTrue(FIELD_TYPE.VAR_STRING == sv.STRING)
|
|
21
|
+
|
|
22
|
+
def test_set_inequality_membership(self):
|
|
23
|
+
self.assertTrue(FIELD_TYPE.DATE != sv.STRING)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CoreModule(unittest.TestCase):
|
|
27
|
+
"""Core _mysql module features."""
|
|
28
|
+
|
|
29
|
+
def test_NULL(self):
|
|
30
|
+
"""Should have a NULL constant."""
|
|
31
|
+
self.assertEqual(_mysql.NULL, 'NULL')
|
|
32
|
+
|
|
33
|
+
def test_version(self):
|
|
34
|
+
"""Version information sanity."""
|
|
35
|
+
self.assertTrue(isinstance(_mysql.__version__, str))
|
|
36
|
+
|
|
37
|
+
self.assertTrue(isinstance(_mysql.version_info, tuple))
|
|
38
|
+
self.assertEqual(len(_mysql.version_info), 5)
|
|
39
|
+
|
|
40
|
+
def test_client_info(self):
|
|
41
|
+
self.assertTrue(isinstance(_mysql.get_client_info(), str))
|
|
42
|
+
|
|
43
|
+
def test_thread_safe(self):
|
|
44
|
+
self.assertTrue(isinstance(_mysql.thread_safe(), int))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CoreAPI(unittest.TestCase):
|
|
48
|
+
"""Test _mysql interaction internals."""
|
|
49
|
+
|
|
50
|
+
def setUp(self):
|
|
51
|
+
kwargs = base.PyMySQLTestCase.databases[0].copy()
|
|
52
|
+
kwargs['read_default_file'] = '~/.my.cnf'
|
|
53
|
+
self.conn = _mysql.connect(**kwargs)
|
|
54
|
+
|
|
55
|
+
def tearDown(self):
|
|
56
|
+
self.conn.close()
|
|
57
|
+
|
|
58
|
+
def test_thread_id(self):
|
|
59
|
+
tid = self.conn.thread_id()
|
|
60
|
+
self.assertTrue(
|
|
61
|
+
isinstance(tid, int), "thread_id didn't return an integral value.",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
self.assertRaises(
|
|
65
|
+
TypeError,
|
|
66
|
+
self.conn.thread_id,
|
|
67
|
+
('evil',),
|
|
68
|
+
"thread_id shouldn't accept arguments.",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def test_affected_rows(self):
|
|
72
|
+
self.assertEqual(
|
|
73
|
+
self.conn.affected_rows(), 0, 'Should return 0 before we do anything.',
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# def test_debug(self):
|
|
77
|
+
# FIXME Only actually tests if you lack SUPER
|
|
78
|
+
# self.assertRaises(sv.OperationalError,
|
|
79
|
+
# self.conn.dump_debug_info)
|
|
80
|
+
|
|
81
|
+
def test_charset_name(self):
|
|
82
|
+
self.assertTrue(
|
|
83
|
+
isinstance(self.conn.character_set_name(), str), 'Should return a string.',
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def test_host_info(self):
|
|
87
|
+
assert isinstance(self.conn.get_host_info(), str), 'should return a string'
|
|
88
|
+
|
|
89
|
+
def test_proto_info(self):
|
|
90
|
+
self.assertTrue(
|
|
91
|
+
isinstance(self.conn.get_proto_info(), int), 'Should return an int.',
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def test_server_info(self):
|
|
95
|
+
self.assertTrue(
|
|
96
|
+
isinstance(self.conn.get_server_info(), str), 'Should return an str.',
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == '__main__':
|
|
101
|
+
unittest.main()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from datetime import time
|
|
4
|
+
from datetime import timedelta
|
|
5
|
+
from time import localtime
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Date = date
|
|
9
|
+
Time = time
|
|
10
|
+
TimeDelta = timedelta
|
|
11
|
+
Timestamp = datetime
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def DateFromTicks(ticks: int) -> date:
|
|
15
|
+
return date(*localtime(ticks)[:3])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def TimeFromTicks(ticks: int) -> time:
|
|
19
|
+
return time(*localtime(ticks)[3:6])
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def TimestampFromTicks(ticks: int) -> datetime:
|
|
23
|
+
return datetime(*localtime(ticks)[:6])
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import os as _os
|
|
3
|
+
import warnings as _warnings
|
|
4
|
+
|
|
5
|
+
from ._objects import organization # noqa: F401
|
|
6
|
+
from ._objects import secrets # noqa: F401
|
|
7
|
+
from ._objects import stage # noqa: F401
|
|
8
|
+
from ._objects import workspace # noqa: F401
|
|
9
|
+
from ._objects import workspace_group # noqa: F401
|
|
10
|
+
from ._portal import portal # noqa: F401
|
|
11
|
+
|
|
12
|
+
if 'SINGLESTOREDB_ORGANIZATION' not in _os.environ:
|
|
13
|
+
_warnings.warn(
|
|
14
|
+
'This package is intended for use in the SingleStoreDB notebook environment',
|
|
15
|
+
RuntimeWarning,
|
|
16
|
+
)
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import functools
|
|
3
|
+
from typing import Any
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from ..management import workspace as _ws
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Secrets(object):
|
|
10
|
+
"""Wrapper for accessing secrets as object attributes."""
|
|
11
|
+
|
|
12
|
+
def __getattr__(self, name: str) -> Optional[str]:
|
|
13
|
+
if name.startswith('_ipython') or name.startswith('_repr_'):
|
|
14
|
+
raise AttributeError(name)
|
|
15
|
+
return _ws.get_secret(name)
|
|
16
|
+
|
|
17
|
+
def __getitem__(self, name: str) -> Optional[str]:
|
|
18
|
+
return _ws.get_secret(name)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Stage(object):
|
|
22
|
+
|
|
23
|
+
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
|
24
|
+
# We are remapping the methods and attributes here so that
|
|
25
|
+
# autocomplete still works in Jupyter / IPython, but we
|
|
26
|
+
# bypass the real method / attribute calls and apply them
|
|
27
|
+
# to the currently selected stage.
|
|
28
|
+
for name in [x for x in dir(_ws.Stage) if not x.startswith('_')]:
|
|
29
|
+
if name in ['from_dict', 'refresh', 'update']:
|
|
30
|
+
continue
|
|
31
|
+
attr = getattr(_ws.Stage, name)
|
|
32
|
+
|
|
33
|
+
def make_wrapper(m: str, is_method: bool = False) -> Any:
|
|
34
|
+
if is_method:
|
|
35
|
+
def wrap(self: Stage, *a: Any, **kw: Any) -> Any:
|
|
36
|
+
return getattr(_ws.get_stage(), m)(*a, **kw)
|
|
37
|
+
return functools.update_wrapper(wrap, attr)
|
|
38
|
+
else:
|
|
39
|
+
def wrap(self: Stage, *a: Any, **kw: Any) -> Any:
|
|
40
|
+
return getattr(_ws.get_stage(), m)
|
|
41
|
+
return property(functools.update_wrapper(wrap, attr))
|
|
42
|
+
|
|
43
|
+
setattr(cls, name, make_wrapper(m=name, is_method=callable(attr)))
|
|
44
|
+
|
|
45
|
+
for name in [
|
|
46
|
+
x for x in _ws.Stage.__annotations__.keys()
|
|
47
|
+
if not x.startswith('_')
|
|
48
|
+
]:
|
|
49
|
+
|
|
50
|
+
def make_wrapper(m: str, is_method: bool = False) -> Any:
|
|
51
|
+
def wrap(self: Stage) -> Any:
|
|
52
|
+
return getattr(_ws.get_stage(), m)
|
|
53
|
+
return property(functools.update_wrapper(wrap, attr))
|
|
54
|
+
|
|
55
|
+
setattr(cls, name, make_wrapper(m=name))
|
|
56
|
+
|
|
57
|
+
cls.__doc__ = _ws.Stage.__doc__
|
|
58
|
+
|
|
59
|
+
return super().__new__(cls, *args, **kwargs)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class WorkspaceGroup(object):
|
|
63
|
+
|
|
64
|
+
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
|
65
|
+
# We are remapping the methods and attributes here so that
|
|
66
|
+
# autocomplete still works in Jupyter / IPython, but we
|
|
67
|
+
# bypass the real method / attribute calls and apply them
|
|
68
|
+
# to the currently selected workspace group.
|
|
69
|
+
for name in [x for x in dir(_ws.WorkspaceGroup) if not x.startswith('_')]:
|
|
70
|
+
if name in ['from_dict', 'refresh', 'update']:
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
attr = getattr(_ws.WorkspaceGroup, name)
|
|
74
|
+
|
|
75
|
+
def make_wrapper(m: str, is_method: bool = False) -> Any:
|
|
76
|
+
if is_method:
|
|
77
|
+
def wrap(self: WorkspaceGroup, *a: Any, **kw: Any) -> Any:
|
|
78
|
+
return getattr(_ws.get_workspace_group(), m)(*a, **kw)
|
|
79
|
+
return functools.update_wrapper(wrap, attr)
|
|
80
|
+
else:
|
|
81
|
+
def wrap(self: WorkspaceGroup, *a: Any, **kw: Any) -> Any:
|
|
82
|
+
return getattr(_ws.get_workspace_group(), m)
|
|
83
|
+
return property(functools.update_wrapper(wrap, attr))
|
|
84
|
+
|
|
85
|
+
setattr(cls, name, make_wrapper(m=name, is_method=callable(attr)))
|
|
86
|
+
|
|
87
|
+
for name in [
|
|
88
|
+
x for x in _ws.WorkspaceGroup.__annotations__.keys()
|
|
89
|
+
if not x.startswith('_')
|
|
90
|
+
]:
|
|
91
|
+
|
|
92
|
+
def make_wrapper(m: str, is_method: bool = False) -> Any:
|
|
93
|
+
def wrap(self: WorkspaceGroup) -> Any:
|
|
94
|
+
return getattr(_ws.get_workspace_group(), m)
|
|
95
|
+
return property(functools.update_wrapper(wrap, attr))
|
|
96
|
+
|
|
97
|
+
setattr(cls, name, make_wrapper(m=name))
|
|
98
|
+
|
|
99
|
+
cls.__doc__ = _ws.WorkspaceGroup.__doc__
|
|
100
|
+
|
|
101
|
+
return super().__new__(cls, *args, **kwargs)
|
|
102
|
+
|
|
103
|
+
def __str__(self) -> str:
|
|
104
|
+
return _ws.get_workspace_group().__str__()
|
|
105
|
+
|
|
106
|
+
def __repr__(self) -> str:
|
|
107
|
+
return _ws.get_workspace_group().__repr__()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Workspace(object):
|
|
111
|
+
|
|
112
|
+
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
|
113
|
+
# We are remapping the methods and attributes here so that
|
|
114
|
+
# autocomplete still works in Jupyter / IPython, but we
|
|
115
|
+
# bypass the real method / attribute calls and apply them
|
|
116
|
+
# to the currently selected workspace.
|
|
117
|
+
for name in [x for x in dir(_ws.Workspace) if not x.startswith('_')]:
|
|
118
|
+
if name in ['from_dict', 'refresh', 'update']:
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
attr = getattr(_ws.Workspace, name)
|
|
122
|
+
|
|
123
|
+
def make_wrapper(m: str, is_method: bool = False) -> Any:
|
|
124
|
+
if is_method:
|
|
125
|
+
def wrap(self: Workspace, *a: Any, **kw: Any) -> Any:
|
|
126
|
+
return getattr(_ws.get_workspace(), m)(*a, **kw)
|
|
127
|
+
return functools.update_wrapper(wrap, attr)
|
|
128
|
+
else:
|
|
129
|
+
def wrap(self: Workspace, *a: Any, **kw: Any) -> Any:
|
|
130
|
+
return getattr(_ws.get_workspace(), m)
|
|
131
|
+
return property(functools.update_wrapper(wrap, attr))
|
|
132
|
+
|
|
133
|
+
setattr(cls, name, make_wrapper(m=name, is_method=callable(attr)))
|
|
134
|
+
|
|
135
|
+
for name in [
|
|
136
|
+
x for x in _ws.Workspace.__annotations__.keys()
|
|
137
|
+
if not x.startswith('_')
|
|
138
|
+
]:
|
|
139
|
+
|
|
140
|
+
def make_wrapper(m: str, is_method: bool = False) -> Any:
|
|
141
|
+
def wrap(self: Workspace) -> Any:
|
|
142
|
+
return getattr(_ws.get_workspace(), m)
|
|
143
|
+
return property(functools.update_wrapper(wrap, attr))
|
|
144
|
+
|
|
145
|
+
setattr(cls, name, make_wrapper(m=name))
|
|
146
|
+
|
|
147
|
+
cls.__doc__ = _ws.Workspace.__doc__
|
|
148
|
+
|
|
149
|
+
return super().__new__(cls, *args, **kwargs)
|
|
150
|
+
|
|
151
|
+
def __str__(self) -> str:
|
|
152
|
+
return _ws.get_workspace().__str__()
|
|
153
|
+
|
|
154
|
+
def __repr__(self) -> str:
|
|
155
|
+
return _ws.get_workspace().__repr__()
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class Organization(object):
|
|
159
|
+
|
|
160
|
+
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
|
161
|
+
# We are remapping the methods and attributes here so that
|
|
162
|
+
# autocomplete still works in Jupyter / IPython, but we
|
|
163
|
+
# bypass the real method / attribute calls and apply them
|
|
164
|
+
# to the currently selected organization.
|
|
165
|
+
for name in [x for x in dir(_ws.Organization) if not x.startswith('_')]:
|
|
166
|
+
if name in ['from_dict', 'refresh', 'update']:
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
attr = getattr(_ws.Organization, name)
|
|
170
|
+
|
|
171
|
+
def make_wrapper(m: str, is_method: bool = False) -> Any:
|
|
172
|
+
if is_method:
|
|
173
|
+
def wrap(self: Organization, *a: Any, **kw: Any) -> Any:
|
|
174
|
+
return getattr(_ws.get_organization(), m)(*a, **kw)
|
|
175
|
+
return functools.update_wrapper(wrap, attr)
|
|
176
|
+
else:
|
|
177
|
+
def wrap(self: Organization, *a: Any, **kw: Any) -> Any:
|
|
178
|
+
return getattr(_ws.get_organization(), m)
|
|
179
|
+
return property(functools.update_wrapper(wrap, attr))
|
|
180
|
+
|
|
181
|
+
setattr(cls, name, make_wrapper(m=name, is_method=callable(attr)))
|
|
182
|
+
|
|
183
|
+
for name in [
|
|
184
|
+
x for x in _ws.Organization.__annotations__.keys()
|
|
185
|
+
if not x.startswith('_')
|
|
186
|
+
]:
|
|
187
|
+
|
|
188
|
+
def make_wrapper(m: str, is_method: bool = False) -> Any:
|
|
189
|
+
def wrap(self: Organization) -> Any:
|
|
190
|
+
return getattr(_ws.get_organization(), m)
|
|
191
|
+
return property(functools.update_wrapper(wrap, attr))
|
|
192
|
+
|
|
193
|
+
setattr(cls, name, make_wrapper(m=name))
|
|
194
|
+
|
|
195
|
+
cls.__doc__ = _ws.Organization.__doc__
|
|
196
|
+
|
|
197
|
+
return super().__new__(cls, *args, **kwargs)
|
|
198
|
+
|
|
199
|
+
def __str__(self) -> str:
|
|
200
|
+
return _ws.get_organization().__str__()
|
|
201
|
+
|
|
202
|
+
def __repr__(self) -> str:
|
|
203
|
+
return _ws.get_organization().__repr__()
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
secrets = Secrets()
|
|
207
|
+
stage = Stage()
|
|
208
|
+
organization = Organization()
|
|
209
|
+
workspace_group = WorkspaceGroup()
|
|
210
|
+
workspace = Workspace()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
__all__ = ['secrets', 'stage', 'workspace', 'workspace_group', 'organization']
|