singlestoredb 1.8.0__cp38-abi3-win_amd64.whl → 1.10.0__cp38-abi3-win_amd64.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_accel.pyd +0 -0
- singlestoredb/__init__.py +2 -2
- singlestoredb/config.py +6 -0
- singlestoredb/connection.py +11 -0
- singlestoredb/fusion/handler.py +3 -7
- singlestoredb/fusion/handlers/export.py +17 -21
- singlestoredb/fusion/handlers/files.py +690 -0
- singlestoredb/fusion/handlers/stage.py +103 -91
- singlestoredb/fusion/handlers/utils.py +148 -0
- singlestoredb/magics/__init__.py +34 -0
- singlestoredb/magics/run_personal.py +56 -0
- singlestoredb/magics/run_shared.py +53 -0
- singlestoredb/management/__init__.py +1 -0
- singlestoredb/management/cluster.py +2 -1
- singlestoredb/management/export.py +20 -184
- singlestoredb/management/files.py +1039 -0
- singlestoredb/management/manager.py +3 -2
- singlestoredb/management/workspace.py +165 -351
- singlestoredb/mysql/connection.py +9 -1
- singlestoredb/tests/test.ipynb +18 -0
- singlestoredb/tests/test2.ipynb +18 -0
- singlestoredb/tests/test_management.py +273 -1
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.10.0.dist-info}/METADATA +1 -1
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.10.0.dist-info}/RECORD +30 -21
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.10.0.dist-info}/top_level.txt +1 -0
- sqlx/__init__.py +4 -0
- sqlx/magic.py +113 -0
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.10.0.dist-info}/LICENSE +0 -0
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.10.0.dist-info}/WHEEL +0 -0
- {singlestoredb-1.8.0.dist-info → singlestoredb-1.10.0.dist-info}/entry_points.txt +0 -0
|
@@ -226,6 +226,8 @@ class Connection(BaseConnection):
|
|
|
226
226
|
Set to true to check the server certificate's validity.
|
|
227
227
|
ssl_verify_identity : bool, optional
|
|
228
228
|
Set to true to check the server's identity.
|
|
229
|
+
tls_sni_servername: str, optional
|
|
230
|
+
Set server host name for TLS connection
|
|
229
231
|
read_default_group : str, optional
|
|
230
232
|
Group to read from in the configuration file.
|
|
231
233
|
autocommit : bool, optional
|
|
@@ -295,6 +297,7 @@ class Connection(BaseConnection):
|
|
|
295
297
|
_auth_plugin_name = ''
|
|
296
298
|
_closed = False
|
|
297
299
|
_secure = False
|
|
300
|
+
_tls_sni_servername = None
|
|
298
301
|
|
|
299
302
|
def __init__( # noqa: C901
|
|
300
303
|
self,
|
|
@@ -335,6 +338,7 @@ class Connection(BaseConnection):
|
|
|
335
338
|
ssl_key=None,
|
|
336
339
|
ssl_verify_cert=None,
|
|
337
340
|
ssl_verify_identity=None,
|
|
341
|
+
tls_sni_servername=None,
|
|
338
342
|
parse_json=True,
|
|
339
343
|
invalid_values=None,
|
|
340
344
|
pure_python=None,
|
|
@@ -638,6 +642,7 @@ class Connection(BaseConnection):
|
|
|
638
642
|
|
|
639
643
|
self._is_committable = True
|
|
640
644
|
self._in_sync = False
|
|
645
|
+
self._tls_sni_servername = tls_sni_servername
|
|
641
646
|
self._track_env = bool(track_env) or self.host == 'singlestore.com'
|
|
642
647
|
self._enable_extended_data_types = enable_extended_data_types
|
|
643
648
|
if vector_data_format.lower() in ['json', 'binary']:
|
|
@@ -1364,7 +1369,10 @@ class Connection(BaseConnection):
|
|
|
1364
1369
|
if self.ssl and self.server_capabilities & CLIENT.SSL:
|
|
1365
1370
|
self.write_packet(data_init)
|
|
1366
1371
|
|
|
1367
|
-
|
|
1372
|
+
hostname = self.host
|
|
1373
|
+
if self._tls_sni_servername:
|
|
1374
|
+
hostname = self._tls_sni_servername
|
|
1375
|
+
self._sock = self.ctx.wrap_socket(self._sock, server_hostname=hostname)
|
|
1368
1376
|
self._rfile = self._sock.makefile('rb')
|
|
1369
1377
|
self._secure = True
|
|
1370
1378
|
|
|
@@ -740,7 +740,7 @@ class TestStage(unittest.TestCase):
|
|
|
740
740
|
'rename_test_2/nest_1/nested_rename_test_3.sql', overwrite=True,
|
|
741
741
|
)
|
|
742
742
|
|
|
743
|
-
def
|
|
743
|
+
def test_file_object(self):
|
|
744
744
|
st = self.wg.stage
|
|
745
745
|
|
|
746
746
|
st.mkdir('obj_test')
|
|
@@ -1028,3 +1028,275 @@ class TestJob(unittest.TestCase):
|
|
|
1028
1028
|
assert deleted
|
|
1029
1029
|
job = job_manager.get(job.job_id)
|
|
1030
1030
|
assert job.terminated_at is not None
|
|
1031
|
+
|
|
1032
|
+
|
|
1033
|
+
@pytest.mark.management
|
|
1034
|
+
class TestFileSpaces(unittest.TestCase):
|
|
1035
|
+
|
|
1036
|
+
manager = None
|
|
1037
|
+
personal_space = None
|
|
1038
|
+
shared_space = None
|
|
1039
|
+
|
|
1040
|
+
@classmethod
|
|
1041
|
+
def setUpClass(cls):
|
|
1042
|
+
cls.manager = s2.manage_files()
|
|
1043
|
+
cls.personal_space = cls.manager.personal_space
|
|
1044
|
+
cls.shared_space = cls.manager.shared_space
|
|
1045
|
+
|
|
1046
|
+
@classmethod
|
|
1047
|
+
def tearDownClass(cls):
|
|
1048
|
+
cls.manager = None
|
|
1049
|
+
cls.personal_space = None
|
|
1050
|
+
cls.shared_space = None
|
|
1051
|
+
|
|
1052
|
+
def test_upload_file(self):
|
|
1053
|
+
for space in [self.personal_space, self.shared_space]:
|
|
1054
|
+
root = space.info('/')
|
|
1055
|
+
assert str(root.path) == '/'
|
|
1056
|
+
assert root.type == 'directory'
|
|
1057
|
+
|
|
1058
|
+
# Upload files
|
|
1059
|
+
f = space.upload_file(
|
|
1060
|
+
TEST_DIR / 'test.ipynb',
|
|
1061
|
+
'upload_test.ipynb',
|
|
1062
|
+
)
|
|
1063
|
+
assert str(f.path) == 'upload_test.ipynb'
|
|
1064
|
+
assert f.type == 'notebook'
|
|
1065
|
+
|
|
1066
|
+
# Download and compare to original
|
|
1067
|
+
txt = f.download(encoding='utf-8')
|
|
1068
|
+
assert txt == open(TEST_DIR / 'test.ipynb').read()
|
|
1069
|
+
|
|
1070
|
+
# Make sure we can't overwrite
|
|
1071
|
+
with self.assertRaises(OSError):
|
|
1072
|
+
space.upload_file(
|
|
1073
|
+
TEST_DIR / 'test.ipynb',
|
|
1074
|
+
'upload_test.ipynb',
|
|
1075
|
+
)
|
|
1076
|
+
|
|
1077
|
+
# Force overwrite with new content
|
|
1078
|
+
f = space.upload_file(
|
|
1079
|
+
TEST_DIR / 'test2.ipynb',
|
|
1080
|
+
'upload_test.ipynb', overwrite=True,
|
|
1081
|
+
)
|
|
1082
|
+
assert str(f.path) == 'upload_test.ipynb'
|
|
1083
|
+
assert f.type == 'notebook'
|
|
1084
|
+
|
|
1085
|
+
# Verify new content
|
|
1086
|
+
txt = f.download(encoding='utf-8')
|
|
1087
|
+
assert txt == open(TEST_DIR / 'test2.ipynb').read()
|
|
1088
|
+
|
|
1089
|
+
# Make sure we can't upload a folder
|
|
1090
|
+
with self.assertRaises(s2.ManagementError):
|
|
1091
|
+
space.upload_folder(TEST_DIR, 'test')
|
|
1092
|
+
|
|
1093
|
+
# Cleanup
|
|
1094
|
+
space.remove('upload_test.ipynb')
|
|
1095
|
+
|
|
1096
|
+
def test_open(self):
|
|
1097
|
+
for space in [self.personal_space, self.shared_space]:
|
|
1098
|
+
# See if error is raised for non-existent file
|
|
1099
|
+
with self.assertRaises(s2.ManagementError):
|
|
1100
|
+
space.open('open_test.ipynb', 'r')
|
|
1101
|
+
|
|
1102
|
+
# Load test file
|
|
1103
|
+
space.upload_file(TEST_DIR / 'test.ipynb', 'open_test.ipynb')
|
|
1104
|
+
|
|
1105
|
+
# Read file using `open`
|
|
1106
|
+
with space.open('open_test.ipynb', 'r') as rfile:
|
|
1107
|
+
assert rfile.read() == open(TEST_DIR / 'test.ipynb').read()
|
|
1108
|
+
|
|
1109
|
+
# Read file using `open` with 'rt' mode
|
|
1110
|
+
with space.open('open_test.ipynb', 'rt') as rfile:
|
|
1111
|
+
assert rfile.read() == open(TEST_DIR / 'test.ipynb').read()
|
|
1112
|
+
|
|
1113
|
+
# Read file using `open` with 'rb' mode
|
|
1114
|
+
with space.open('open_test.ipynb', 'rb') as rfile:
|
|
1115
|
+
assert rfile.read() == open(TEST_DIR / 'test.ipynb', 'rb').read()
|
|
1116
|
+
|
|
1117
|
+
# Read file using `open` with 'rb' mode
|
|
1118
|
+
with self.assertRaises(ValueError):
|
|
1119
|
+
with space.open('open_test.ipynb', 'b') as rfile:
|
|
1120
|
+
pass
|
|
1121
|
+
|
|
1122
|
+
# Attempt overwrite file using `open` with mode 'x'
|
|
1123
|
+
with self.assertRaises(OSError):
|
|
1124
|
+
with space.open('open_test.ipynb', 'x') as wfile:
|
|
1125
|
+
pass
|
|
1126
|
+
|
|
1127
|
+
# Attempt overwrite file using `open` with mode 'w'
|
|
1128
|
+
with space.open('open_test.ipynb', 'w') as wfile:
|
|
1129
|
+
wfile.write(open(TEST_DIR / 'test2.ipynb').read())
|
|
1130
|
+
|
|
1131
|
+
txt = space.download_file('open_test.ipynb', encoding='utf-8')
|
|
1132
|
+
|
|
1133
|
+
assert txt == open(TEST_DIR / 'test2.ipynb').read()
|
|
1134
|
+
|
|
1135
|
+
# Test writer without context manager
|
|
1136
|
+
wfile = space.open('open_raw_test.ipynb', 'w')
|
|
1137
|
+
for line in open(TEST_DIR / 'test.ipynb'):
|
|
1138
|
+
wfile.write(line)
|
|
1139
|
+
wfile.close()
|
|
1140
|
+
|
|
1141
|
+
txt = space.download_file(
|
|
1142
|
+
'open_raw_test.ipynb',
|
|
1143
|
+
encoding='utf-8',
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
assert txt == open(TEST_DIR / 'test.ipynb').read()
|
|
1147
|
+
|
|
1148
|
+
# Test reader without context manager
|
|
1149
|
+
rfile = space.open('open_raw_test.ipynb', 'r')
|
|
1150
|
+
txt = ''
|
|
1151
|
+
for line in rfile:
|
|
1152
|
+
txt += line
|
|
1153
|
+
rfile.close()
|
|
1154
|
+
|
|
1155
|
+
assert txt == open(TEST_DIR / 'test.ipynb').read()
|
|
1156
|
+
|
|
1157
|
+
# Cleanup
|
|
1158
|
+
space.remove('open_test.ipynb')
|
|
1159
|
+
space.remove('open_raw_test.ipynb')
|
|
1160
|
+
|
|
1161
|
+
def test_obj_open(self):
|
|
1162
|
+
for space in [self.personal_space, self.shared_space]:
|
|
1163
|
+
# Load test file
|
|
1164
|
+
f = space.upload_file(
|
|
1165
|
+
TEST_DIR / 'test.ipynb',
|
|
1166
|
+
'obj_open_test.ipynb',
|
|
1167
|
+
)
|
|
1168
|
+
|
|
1169
|
+
# Read file using `open`
|
|
1170
|
+
with f.open() as rfile:
|
|
1171
|
+
assert rfile.read() == open(TEST_DIR / 'test.ipynb').read()
|
|
1172
|
+
|
|
1173
|
+
# Make sure directories error out
|
|
1174
|
+
with self.assertRaises(s2.ManagementError):
|
|
1175
|
+
space.mkdir('obj_open_dir')
|
|
1176
|
+
|
|
1177
|
+
# Write file using `open`
|
|
1178
|
+
with f.open('w', encoding='utf-8') as wfile:
|
|
1179
|
+
wfile.write(open(TEST_DIR / 'test2.ipynb').read())
|
|
1180
|
+
|
|
1181
|
+
assert f.download(encoding='utf-8') == open(TEST_DIR / 'test2.ipynb').read()
|
|
1182
|
+
|
|
1183
|
+
# Test writer without context manager
|
|
1184
|
+
wfile = f.open('w')
|
|
1185
|
+
for line in open(TEST_DIR / 'test.ipynb'):
|
|
1186
|
+
wfile.write(line)
|
|
1187
|
+
wfile.close()
|
|
1188
|
+
|
|
1189
|
+
txt = space.download_file(f.path, encoding='utf-8')
|
|
1190
|
+
|
|
1191
|
+
assert txt == open(TEST_DIR / 'test.ipynb').read()
|
|
1192
|
+
|
|
1193
|
+
# Test reader without context manager
|
|
1194
|
+
rfile = f.open('r')
|
|
1195
|
+
txt = ''
|
|
1196
|
+
for line in rfile:
|
|
1197
|
+
txt += line
|
|
1198
|
+
rfile.close()
|
|
1199
|
+
|
|
1200
|
+
assert txt == open(TEST_DIR / 'test.ipynb').read()
|
|
1201
|
+
|
|
1202
|
+
# Cleanup
|
|
1203
|
+
space.remove('obj_open_test.ipynb')
|
|
1204
|
+
|
|
1205
|
+
def test_os_directories(self):
|
|
1206
|
+
for space in [self.personal_space, self.shared_space]:
|
|
1207
|
+
# Make sure directories error out
|
|
1208
|
+
with self.assertRaises(s2.ManagementError):
|
|
1209
|
+
space.mkdir('mkdir_test_1')
|
|
1210
|
+
|
|
1211
|
+
with self.assertRaises(s2.ManagementError):
|
|
1212
|
+
space.exists('mkdir_test_1/')
|
|
1213
|
+
|
|
1214
|
+
out = space.listdir('/')
|
|
1215
|
+
assert 'mkdir_test_1/' not in out
|
|
1216
|
+
|
|
1217
|
+
with self.assertRaises(s2.ManagementError):
|
|
1218
|
+
space.rmdir('mkdir_test_1/')
|
|
1219
|
+
|
|
1220
|
+
def test_os_rename(self):
|
|
1221
|
+
for space in [self.personal_space, self.shared_space]:
|
|
1222
|
+
space.upload_file(
|
|
1223
|
+
TEST_DIR / 'test.ipynb',
|
|
1224
|
+
'rename_test.ipynb',
|
|
1225
|
+
)
|
|
1226
|
+
assert 'rename_test.ipynb' in space.listdir('/')
|
|
1227
|
+
assert 'rename_test_2.ipynb' not in space.listdir('/')
|
|
1228
|
+
|
|
1229
|
+
space.rename(
|
|
1230
|
+
'rename_test.ipynb',
|
|
1231
|
+
'rename_test_2.ipynb',
|
|
1232
|
+
)
|
|
1233
|
+
assert 'rename_test.ipynb' not in space.listdir('/')
|
|
1234
|
+
assert 'rename_test_2.ipynb' in space.listdir('/')
|
|
1235
|
+
|
|
1236
|
+
# non-existent file
|
|
1237
|
+
with self.assertRaises(OSError):
|
|
1238
|
+
space.rename('rename_foo.ipynb', 'rename_foo_2.ipynb')
|
|
1239
|
+
|
|
1240
|
+
space.upload_file(
|
|
1241
|
+
TEST_DIR / 'test.ipynb',
|
|
1242
|
+
'rename_test_3.ipynb',
|
|
1243
|
+
)
|
|
1244
|
+
|
|
1245
|
+
# overwrite
|
|
1246
|
+
with self.assertRaises(OSError):
|
|
1247
|
+
space.rename(
|
|
1248
|
+
'rename_test_2.ipynb',
|
|
1249
|
+
'rename_test_3.ipynb',
|
|
1250
|
+
)
|
|
1251
|
+
|
|
1252
|
+
space.rename(
|
|
1253
|
+
'rename_test_2.ipynb',
|
|
1254
|
+
'rename_test_3.ipynb', overwrite=True,
|
|
1255
|
+
)
|
|
1256
|
+
|
|
1257
|
+
# Cleanup
|
|
1258
|
+
space.remove('rename_test_3.ipynb')
|
|
1259
|
+
|
|
1260
|
+
def test_file_object(self):
|
|
1261
|
+
for space in [self.personal_space, self.shared_space]:
|
|
1262
|
+
f = space.upload_file(
|
|
1263
|
+
TEST_DIR / 'test.ipynb',
|
|
1264
|
+
'obj_test.ipynb',
|
|
1265
|
+
)
|
|
1266
|
+
|
|
1267
|
+
assert not f.is_dir()
|
|
1268
|
+
assert f.is_file()
|
|
1269
|
+
|
|
1270
|
+
# abspath / basename / dirname / exists
|
|
1271
|
+
assert f.abspath() == 'obj_test.ipynb'
|
|
1272
|
+
assert f.basename() == 'obj_test.ipynb'
|
|
1273
|
+
assert f.dirname() == '/'
|
|
1274
|
+
assert f.exists()
|
|
1275
|
+
|
|
1276
|
+
# download
|
|
1277
|
+
assert f.download(encoding='utf-8') == \
|
|
1278
|
+
open(TEST_DIR / 'test.ipynb', 'r').read()
|
|
1279
|
+
assert f.download() == open(TEST_DIR / 'test.ipynb', 'rb').read()
|
|
1280
|
+
|
|
1281
|
+
assert space.is_file('obj_test.ipynb')
|
|
1282
|
+
f.remove()
|
|
1283
|
+
assert not space.is_file('obj_test.ipynb')
|
|
1284
|
+
|
|
1285
|
+
# mtime / ctime
|
|
1286
|
+
assert f.getmtime() > 0
|
|
1287
|
+
assert f.getctime() > 0
|
|
1288
|
+
|
|
1289
|
+
# rename
|
|
1290
|
+
f = space.upload_file(
|
|
1291
|
+
TEST_DIR / 'test.ipynb',
|
|
1292
|
+
'obj_test.ipynb',
|
|
1293
|
+
)
|
|
1294
|
+
assert space.exists('obj_test.ipynb')
|
|
1295
|
+
assert not space.exists('obj_test_2.ipynb')
|
|
1296
|
+
f.rename('obj_test_2.ipynb')
|
|
1297
|
+
assert not space.exists('obj_test.ipynb')
|
|
1298
|
+
assert space.exists('obj_test_2.ipynb')
|
|
1299
|
+
assert f.abspath() == 'obj_test_2.ipynb'
|
|
1300
|
+
|
|
1301
|
+
# Cleanup
|
|
1302
|
+
space.remove('obj_test_2.ipynb')
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
_singlestoredb_accel.pyd,sha256=
|
|
2
|
-
singlestoredb/__init__.py,sha256=
|
|
1
|
+
_singlestoredb_accel.pyd,sha256=cTNWxsBsHk77ryPm-wj9wFEi3q0MJgQ5JFOGo8KsfdM,59392
|
|
2
|
+
singlestoredb/__init__.py,sha256=tic3Nv74hrvLzFMKed_-mr9zHudZrMogLBcwzs988lk,1712
|
|
3
3
|
singlestoredb/auth.py,sha256=RmYiH0Wlc2RXc4pTlRMysxtBI445ggCIwojWKC_eDLE,7844
|
|
4
|
-
singlestoredb/config.py,sha256=
|
|
5
|
-
singlestoredb/connection.py,sha256=
|
|
4
|
+
singlestoredb/config.py,sha256=n6ludREIoiZDEzSGmv0xouv_zHFnznKNKxSvjzgQ3Lk,12876
|
|
5
|
+
singlestoredb/connection.py,sha256=I3A0VkA_E6pfUNCzIvj0KTb5zn6-htBAPsu6OVXO9-I,47150
|
|
6
6
|
singlestoredb/converters.py,sha256=7_Of1f3Ow-JUoY1pHFlMPYxvt8llzdk-7X8qk5z2xJM,21631
|
|
7
7
|
singlestoredb/exceptions.py,sha256=WCCJrNSsU-hD-621Jpd6bwmvGftQ7byXkk-XKXlaxpg,3354
|
|
8
8
|
singlestoredb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -32,31 +32,36 @@ singlestoredb/functions/ext/rowdat_1.py,sha256=KYj_y5JWm3_B2-QC47HK-CNOrzujBqGUw
|
|
|
32
32
|
singlestoredb/functions/ext/utils.py,sha256=OPMFD-tTCx2Kk9jguQkrTr7e4AgNkt15YsvaT1YSmN8,5480
|
|
33
33
|
singlestoredb/fusion/__init__.py,sha256=FHWtrg6OJFTf6Ye197V5sU6ssryr2h6FBcDIgXP7-H4,367
|
|
34
34
|
singlestoredb/fusion/graphql.py,sha256=SHqsPe4xgawdsTPHEtJGQlybYGWqPrGMmyK-v20RLac,5420
|
|
35
|
-
singlestoredb/fusion/handler.py,sha256=
|
|
35
|
+
singlestoredb/fusion/handler.py,sha256=ohnU0BIoJ9AHrVLlCHI-3E4Icqoocxqip8T-XyYxBWQ,28293
|
|
36
36
|
singlestoredb/fusion/registry.py,sha256=_eT1gd38VPlFKs5f9Pu6lqQyoDQ_ixW5O56QwYLQ89Y,6361
|
|
37
37
|
singlestoredb/fusion/result.py,sha256=EcFY5Qv43ySlQsfl_JB-I3ko7PzVdjuhhoKN96uHSAM,12171
|
|
38
38
|
singlestoredb/fusion/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
-
singlestoredb/fusion/handlers/export.py,sha256=
|
|
39
|
+
singlestoredb/fusion/handlers/export.py,sha256=Vf0idiFTluEhDpt7ozWSdYwQcOeXz2-J6-91ax8F834,7189
|
|
40
|
+
singlestoredb/fusion/handlers/files.py,sha256=sXO5OFS5QsmwNOnMY3G5xpMPqh4_WkssW3dOB6uI4VQ,19669
|
|
40
41
|
singlestoredb/fusion/handlers/job.py,sha256=3enfxHwERH7T4u0FEwOPN0IL0GtepaCYgEsisiy3Df4,21753
|
|
41
|
-
singlestoredb/fusion/handlers/stage.py,sha256=
|
|
42
|
-
singlestoredb/fusion/handlers/utils.py,sha256=
|
|
42
|
+
singlestoredb/fusion/handlers/stage.py,sha256=PP-SSP204lwpmnycSXXSmFPzoN535JVuwglDCbaQ8Lw,14789
|
|
43
|
+
singlestoredb/fusion/handlers/utils.py,sha256=OffWWj8FkZgqr5ijY3WQ6cwaAB7a7Z5cYxLeOAfU_JE,10356
|
|
43
44
|
singlestoredb/fusion/handlers/workspace.py,sha256=NxoEY5xd5lCQmXiim4nhAYCL0agHo1H_rGPpqa31hiw,28397
|
|
44
45
|
singlestoredb/http/__init__.py,sha256=4cEDvLloGc3LSpU-PnIwacyu0n5oIIIE6xk2SPyWD_w,939
|
|
45
46
|
singlestoredb/http/connection.py,sha256=LFUeWx7maS7xhQLqEX3pgvIGoosqyJTovtWwJ1DyDSA,40721
|
|
46
|
-
singlestoredb/
|
|
47
|
+
singlestoredb/magics/__init__.py,sha256=fqCBQ0s8o1CYE4Xo_XiSbkLDzLgMNDgpSkOx66-uDZw,1244
|
|
48
|
+
singlestoredb/magics/run_personal.py,sha256=M11xHi9lWquh_pLSpFI89LGE7PhOPQOGqlSPDl48itE,1900
|
|
49
|
+
singlestoredb/magics/run_shared.py,sha256=rnKpW4d8CJvD6ehK8jG8FlxuqZvjZl4KocPTsk-23O8,1805
|
|
50
|
+
singlestoredb/management/__init__.py,sha256=A66ZnFyX--PsAZ2tvtYUfIUBvVGDBFQsnVc6nGTlX60,277
|
|
47
51
|
singlestoredb/management/billing_usage.py,sha256=0UHFSPCrN0nyeGFFM-HXS3NP8pYmYo2BCCahDEPXvzg,3883
|
|
48
|
-
singlestoredb/management/cluster.py,sha256=
|
|
49
|
-
singlestoredb/management/export.py,sha256=
|
|
52
|
+
singlestoredb/management/cluster.py,sha256=auBzNYIXvnI6rq3DNpPgJhwWoT6JsyZRikjpON23Pxg,14867
|
|
53
|
+
singlestoredb/management/export.py,sha256=Ksrb8_sxeqva4NElaGxdPQUWQga2yOEkf0wnw5M-iGc,4302
|
|
54
|
+
singlestoredb/management/files.py,sha256=9uVWe6yc48B9J1GHcMxQ4GEHOnIVlFzEXuKhWFuuZH0,29256
|
|
50
55
|
singlestoredb/management/job.py,sha256=Npfe1JLYJlggGBrXLniPKwKUKF1i3alvSY1SFtvauSs,25498
|
|
51
|
-
singlestoredb/management/manager.py,sha256=
|
|
56
|
+
singlestoredb/management/manager.py,sha256=8zU0d7NG83PYMhoAs2JriTqbqh-R2tLX7VZoeZtcogY,9148
|
|
52
57
|
singlestoredb/management/organization.py,sha256=JBsNC4R3boUKdYvyCZyfGoVMC1mD6SPuMI1UssBVoOM,5611
|
|
53
58
|
singlestoredb/management/region.py,sha256=oGoLLS88dE1GmY7GCc0BV7X3f7bWwKQyeXOVBFmK9Pk,1678
|
|
54
59
|
singlestoredb/management/utils.py,sha256=BP-Wb8Sg16GbdLI_DeBz-3ttMklz6ZjYyMOz-sxElz8,13594
|
|
55
|
-
singlestoredb/management/workspace.py,sha256=
|
|
60
|
+
singlestoredb/management/workspace.py,sha256=erII_7SA4vdJkTPBKB6aa88mYujmXTYdNeQZu8KJKLM,58070
|
|
56
61
|
singlestoredb/mysql/__init__.py,sha256=CbpwzNUJPAmKPpIobC0-ugBta_RgHCMq7X7N75QLReY,4669
|
|
57
62
|
singlestoredb/mysql/_auth.py,sha256=YaqqyvAHmeraBv3BM207rNveUVPM-mPnW20ts_ynVWg,8341
|
|
58
63
|
singlestoredb/mysql/charset.py,sha256=mnCdMpvdub1S2mm2PSk2j5JddgsWRjsVLtGx-y9TskE,10724
|
|
59
|
-
singlestoredb/mysql/connection.py,sha256=
|
|
64
|
+
singlestoredb/mysql/connection.py,sha256=mmTF9X7cPbmTS7yPHHbkjwQGpOPSbpDgZ3MNd2MyXkA,74716
|
|
60
65
|
singlestoredb/mysql/converters.py,sha256=vebFFm6IrC0WgY-5Eh-esaPizY5cq3vDOUlEKGaYM-U,7771
|
|
61
66
|
singlestoredb/mysql/cursors.py,sha256=pkrP-1t8IhBJRnYpdM7Rdm-332nOq1RYTDJ_yg_q5HI,27682
|
|
62
67
|
singlestoredb/mysql/err.py,sha256=aDbmfq08gWVmfgIea735wSeiFdvYbB5wusgd3qTVq1s,2480
|
|
@@ -100,7 +105,9 @@ singlestoredb/notebook/_portal.py,sha256=ip3MkJ51syxppUVGRjgqLfR0He4KCZVklFwWT_X
|
|
|
100
105
|
singlestoredb/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
101
106
|
singlestoredb/tests/empty.sql,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
102
107
|
singlestoredb/tests/local_infile.csv,sha256=0fYxcZcTvcwS2McF4sktFsipRY1G-ClGmCRR1eCqdEQ,45
|
|
108
|
+
singlestoredb/tests/test.ipynb,sha256=IEgXbByXyWDplZvod1J2SqNHZiPOGdD4oNVMd0ArP7s,234
|
|
103
109
|
singlestoredb/tests/test.sql,sha256=winJzhZ2W52PcQ1j8X_NNIDRBmEa-xMAYrS_hoUtAP8,18337
|
|
110
|
+
singlestoredb/tests/test2.ipynb,sha256=_kBQVvEoinQ1zInNcWFKpbdw-djkLsEO8l3g2MEU_Zs,236
|
|
104
111
|
singlestoredb/tests/test2.sql,sha256=CEM8_lX189iQU65G3Pod7lig6osfrveQyoDz6HC35YQ,38
|
|
105
112
|
singlestoredb/tests/test_basics.py,sha256=tLiR46qUy8-OHHRSHsQTBp5q9NAwNPR9HvfutI6YgnM,47629
|
|
106
113
|
singlestoredb/tests/test_config.py,sha256=Ad0PDmCnJMOyy9f7WTKiRasSR_3mYRByUlSb7k5ZySg,11502
|
|
@@ -111,7 +118,7 @@ singlestoredb/tests/test_ext_func.py,sha256=gQErR-wAN8BqLNG5U4pNbg4qkQEo6Re8Hd9_
|
|
|
111
118
|
singlestoredb/tests/test_ext_func_data.py,sha256=9kn8BWmCjkbnP6hSbFhmhcdW4OmVT-GSvBTIzFBLEys,48796
|
|
112
119
|
singlestoredb/tests/test_fusion.py,sha256=ckATjXKcDBvej68PZgTRJirdoywtUJ7WXkZmfR0la_4,24544
|
|
113
120
|
singlestoredb/tests/test_http.py,sha256=7hwXe61hlUes3nji0MTTZweo94tJAlJ-vA5ct9geXFQ,8868
|
|
114
|
-
singlestoredb/tests/test_management.py,sha256=
|
|
121
|
+
singlestoredb/tests/test_management.py,sha256=y6K9dm-s9wxi1yk7fGt-h6hYlB9W4VZy4FgxfVbgZcE,44782
|
|
115
122
|
singlestoredb/tests/test_plugin.py,sha256=P1nXLnTafaHkHN-6bVbGryxTu7OWJPU9SYFZ_WQUwq8,845
|
|
116
123
|
singlestoredb/tests/test_results.py,sha256=Zg1ynZFRZqalAMfNLOU5C6BDXaox6JxrKm_XZwVNFcg,6753
|
|
117
124
|
singlestoredb/tests/test_types.py,sha256=YeVE6KPqlqzJke-4hbRmc8ko1E7RLHu5S8qLg04Bl5Y,4632
|
|
@@ -128,9 +135,11 @@ singlestoredb/utils/events.py,sha256=rC9cHAetua_E1f-EiFkFM-gJzQSQIH5Uk-4sspC3KjI
|
|
|
128
135
|
singlestoredb/utils/mogrify.py,sha256=gCcn99-vgsGVjTUV7RHJ6hH4vCNrsGB_Xo4z8kiSPDQ,4201
|
|
129
136
|
singlestoredb/utils/results.py,sha256=wR70LhCqlobniZf52r67zYLBOKjWHQm68NAskdRQND8,15862
|
|
130
137
|
singlestoredb/utils/xdict.py,sha256=-wi1lSPTnY99fhVMBhPKJ8cCsQhNG4GMUfkEBDKYgCw,13321
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
singlestoredb-1.
|
|
134
|
-
singlestoredb-1.
|
|
135
|
-
singlestoredb-1.
|
|
136
|
-
singlestoredb-1.
|
|
138
|
+
sqlx/__init__.py,sha256=4Sdn8HN-Hf8v0_wCt60DCckCg8BvgM3-9r4YVfZycRE,89
|
|
139
|
+
sqlx/magic.py,sha256=6VBlotgjautjev599tHaTYOfcfOA9m6gV_-P1_Qc4lI,3622
|
|
140
|
+
singlestoredb-1.10.0.dist-info/LICENSE,sha256=Bojenzui8aPNjlF3w4ojguDP7sTf8vFV_9Gc2UAG1sg,11542
|
|
141
|
+
singlestoredb-1.10.0.dist-info/METADATA,sha256=WMO1XteYPWWD3omXLyDKKoz10HINKtWZ5Hqdo75Z8rQ,5711
|
|
142
|
+
singlestoredb-1.10.0.dist-info/WHEEL,sha256=UyMHzmWA0xVqVPKfTiLs2eN3OWWZUl-kQemNbpIqlKo,100
|
|
143
|
+
singlestoredb-1.10.0.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
|
|
144
|
+
singlestoredb-1.10.0.dist-info/top_level.txt,sha256=lA65Vf4qAMfg_s1oG3LEO90h4t1Z-SPDbRqkevI3bSY,40
|
|
145
|
+
singlestoredb-1.10.0.dist-info/RECORD,,
|
sqlx/__init__.py
ADDED
sqlx/magic.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from IPython.core.interactiveshell import InteractiveShell
|
|
6
|
+
from IPython.core.magic import cell_magic
|
|
7
|
+
from IPython.core.magic import line_magic
|
|
8
|
+
from IPython.core.magic import Magics
|
|
9
|
+
from IPython.core.magic import magics_class
|
|
10
|
+
from IPython.core.magic import needs_local_scope
|
|
11
|
+
from IPython.core.magic import no_var_expand
|
|
12
|
+
from sql.magic import SqlMagic
|
|
13
|
+
from sqlalchemy import create_engine
|
|
14
|
+
from sqlalchemy import Engine
|
|
15
|
+
from sqlalchemy import PoolProxiedConnection
|
|
16
|
+
|
|
17
|
+
DEFAULT_POOL_SIZE = 10 # Maximum number of connections in the pool
|
|
18
|
+
DEFAULT_MAX_OVERFLOW = 5 # additional connections (temporary overflow)
|
|
19
|
+
DEFAULT_POOL_TIMEOUT = 30 # Wait time for a connection from the pool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@magics_class
|
|
23
|
+
class SqlxMagic(Magics):
|
|
24
|
+
def __init__(self, shell: InteractiveShell):
|
|
25
|
+
Magics.__init__(self, shell=shell)
|
|
26
|
+
self.magic = SqlMagic(shell)
|
|
27
|
+
self.engine: Optional['Engine'] = None
|
|
28
|
+
|
|
29
|
+
@no_var_expand
|
|
30
|
+
@needs_local_scope
|
|
31
|
+
@line_magic('sqlx')
|
|
32
|
+
@cell_magic('sqlx')
|
|
33
|
+
def sqlx(self, line: str, cell: str = '', local_ns: Any = None) -> Any:
|
|
34
|
+
"""
|
|
35
|
+
Runs SQL statement against a database, specified by
|
|
36
|
+
SQLAlchemy connect string present in DATABASE_URL environment variable.
|
|
37
|
+
|
|
38
|
+
The magic can be used both as a cell magic `%%sqlx` and
|
|
39
|
+
line magic `%sqlx` (see examples below).
|
|
40
|
+
|
|
41
|
+
This is a thin wrapper around the [jupysql](https://jupysql.ploomber.io/) magic,
|
|
42
|
+
allowing multi-threaded execution.
|
|
43
|
+
A connection pool will be maintained internally.
|
|
44
|
+
|
|
45
|
+
Examples::
|
|
46
|
+
|
|
47
|
+
# Line usage
|
|
48
|
+
|
|
49
|
+
%sqlx SELECT * FROM mytable
|
|
50
|
+
|
|
51
|
+
result = %sqlx SELECT 1
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Cell usage
|
|
55
|
+
|
|
56
|
+
%%sqlx
|
|
57
|
+
DELETE FROM mytable
|
|
58
|
+
|
|
59
|
+
%%sqlx
|
|
60
|
+
DROP TABLE mytable
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
connection = self.get_connection()
|
|
65
|
+
try:
|
|
66
|
+
result = self.magic.execute(line, cell, local_ns, connection)
|
|
67
|
+
finally:
|
|
68
|
+
connection.close()
|
|
69
|
+
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
def get_connection(self) -> PoolProxiedConnection:
|
|
73
|
+
if self.engine is None:
|
|
74
|
+
if 'DATABASE_URL' not in os.environ:
|
|
75
|
+
raise RuntimeError(
|
|
76
|
+
'Cannot create connection pool, environment variable'
|
|
77
|
+
" 'DATABASE_URL' is missing.",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# TODO: allow configuring engine
|
|
81
|
+
# idea: %sqlx engine
|
|
82
|
+
# idea: %%sqlx engine
|
|
83
|
+
self.engine = create_engine(
|
|
84
|
+
os.environ['DATABASE_URL'],
|
|
85
|
+
pool_size=DEFAULT_POOL_SIZE,
|
|
86
|
+
max_overflow=DEFAULT_MAX_OVERFLOW,
|
|
87
|
+
pool_timeout=DEFAULT_POOL_TIMEOUT,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return self.engine.raw_connection()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# In order to actually use these magics, you must register them with a
|
|
94
|
+
# running IPython.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def load_ipython_extension(ip: InteractiveShell) -> None:
|
|
98
|
+
"""
|
|
99
|
+
Any module file that define a function named `load_ipython_extension`
|
|
100
|
+
can be loaded via `%load_ext module.path` or be configured to be
|
|
101
|
+
autoloaded by IPython at startup time.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
# Load jupysql extension
|
|
105
|
+
# This is necessary for jupysql to initialize internal state
|
|
106
|
+
# required to render messages
|
|
107
|
+
assert ip.extension_manager is not None
|
|
108
|
+
result = ip.extension_manager.load_extension('sql')
|
|
109
|
+
if result == 'no load function':
|
|
110
|
+
raise RuntimeError('Could not load sql extension. Is jupysql installed?')
|
|
111
|
+
|
|
112
|
+
# Register sqlx
|
|
113
|
+
ip.register_magics(SqlxMagic(ip))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|