singlestoredb 1.1.0__cp38-abi3-win32.whl → 1.3.0__cp38-abi3-win32.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 +1 -1
- singlestoredb/config.py +6 -0
- singlestoredb/connection.py +23 -1
- singlestoredb/converters.py +390 -0
- singlestoredb/functions/ext/asgi.py +7 -1
- singlestoredb/fusion/handler.py +14 -8
- singlestoredb/fusion/handlers/stage.py +167 -84
- singlestoredb/fusion/handlers/workspace.py +250 -108
- singlestoredb/fusion/registry.py +27 -10
- singlestoredb/http/connection.py +18 -1
- singlestoredb/management/__init__.py +1 -0
- singlestoredb/management/organization.py +4 -0
- singlestoredb/management/utils.py +2 -2
- singlestoredb/management/workspace.py +79 -6
- singlestoredb/mysql/connection.py +92 -16
- singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
- singlestoredb/mysql/constants/FIELD_TYPE.py +16 -0
- singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
- singlestoredb/mysql/cursors.py +13 -10
- singlestoredb/mysql/protocol.py +50 -1
- singlestoredb/notebook/__init__.py +15 -0
- singlestoredb/notebook/_objects.py +212 -0
- singlestoredb/tests/test.sql +49 -0
- singlestoredb/tests/test_connection.py +174 -0
- singlestoredb/utils/results.py +5 -1
- {singlestoredb-1.1.0.dist-info → singlestoredb-1.3.0.dist-info}/METADATA +1 -1
- {singlestoredb-1.1.0.dist-info → singlestoredb-1.3.0.dist-info}/RECORD +32 -28
- {singlestoredb-1.1.0.dist-info → singlestoredb-1.3.0.dist-info}/LICENSE +0 -0
- {singlestoredb-1.1.0.dist-info → singlestoredb-1.3.0.dist-info}/WHEEL +0 -0
- {singlestoredb-1.1.0.dist-info → singlestoredb-1.3.0.dist-info}/entry_points.txt +0 -0
- {singlestoredb-1.1.0.dist-info → singlestoredb-1.3.0.dist-info}/top_level.txt +0 -0
|
@@ -121,6 +121,10 @@ class Organization(object):
|
|
|
121
121
|
|
|
122
122
|
"""
|
|
123
123
|
|
|
124
|
+
id: str
|
|
125
|
+
name: str
|
|
126
|
+
firewall_ranges: List[str]
|
|
127
|
+
|
|
124
128
|
def __init__(self, id: str, name: str, firewall_ranges: List[str]):
|
|
125
129
|
"""Use :attr:`WorkspaceManager.organization` instead."""
|
|
126
130
|
#: Unique ID of the organization
|
|
@@ -294,7 +294,7 @@ def snake_to_camel_dict(
|
|
|
294
294
|
return None
|
|
295
295
|
out = {}
|
|
296
296
|
for k, v in s.items():
|
|
297
|
-
if isinstance(
|
|
297
|
+
if isinstance(v, Mapping):
|
|
298
298
|
out[str(snake_to_camel(k))] = snake_to_camel_dict(v, cap_first=cap_first)
|
|
299
299
|
else:
|
|
300
300
|
out[str(snake_to_camel(k))] = v
|
|
@@ -307,7 +307,7 @@ def camel_to_snake_dict(s: Optional[Mapping[str, Any]]) -> Optional[Dict[str, An
|
|
|
307
307
|
return None
|
|
308
308
|
out = {}
|
|
309
309
|
for k, v in s.items():
|
|
310
|
-
if isinstance(
|
|
310
|
+
if isinstance(v, Mapping):
|
|
311
311
|
out[str(camel_to_snake(k))] = camel_to_snake_dict(v)
|
|
312
312
|
else:
|
|
313
313
|
out[str(camel_to_snake(k))] = v
|
|
@@ -34,9 +34,53 @@ from .utils import ttl_property
|
|
|
34
34
|
from .utils import vars_to_str
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def get_organization() -> Organization:
|
|
38
|
+
"""Get the organization."""
|
|
39
|
+
return manage_workspaces().organization
|
|
40
|
+
|
|
41
|
+
|
|
37
42
|
def get_secret(name: str) -> str:
|
|
38
43
|
"""Get a secret from the organization."""
|
|
39
|
-
return
|
|
44
|
+
return get_organization().get_secret(name).value
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_workspace_group(
|
|
48
|
+
workspace_group: Optional[Union[WorkspaceGroup, str]] = None,
|
|
49
|
+
) -> WorkspaceGroup:
|
|
50
|
+
"""Get the stage for the workspace group."""
|
|
51
|
+
if isinstance(workspace_group, WorkspaceGroup):
|
|
52
|
+
return workspace_group
|
|
53
|
+
elif workspace_group:
|
|
54
|
+
return manage_workspaces().workspace_groups[workspace_group]
|
|
55
|
+
elif 'SINGLESTOREDB_WORKSPACE_GROUP' in os.environ:
|
|
56
|
+
return manage_workspaces().workspace_groups[
|
|
57
|
+
os.environ['SINGLESTOREDB_WORKSPACE_GROUP']
|
|
58
|
+
]
|
|
59
|
+
raise RuntimeError('no workspace group specified')
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_stage(
|
|
63
|
+
workspace_group: Optional[Union[WorkspaceGroup, str]] = None,
|
|
64
|
+
) -> Stage:
|
|
65
|
+
"""Get the stage for the workspace group."""
|
|
66
|
+
return get_workspace_group(workspace_group).stage
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_workspace(
|
|
70
|
+
workspace_group: Optional[Union[WorkspaceGroup, str]] = None,
|
|
71
|
+
workspace: Optional[Union[Workspace, str]] = None,
|
|
72
|
+
) -> Workspace:
|
|
73
|
+
"""Get the workspaces for a workspace_group."""
|
|
74
|
+
if isinstance(workspace, Workspace):
|
|
75
|
+
return workspace
|
|
76
|
+
wg = get_workspace_group(workspace_group)
|
|
77
|
+
if workspace:
|
|
78
|
+
return wg.workspaces[workspace]
|
|
79
|
+
elif 'SINGLESTOREDB_WORKSPACE' in os.environ:
|
|
80
|
+
return wg.workspaces[
|
|
81
|
+
os.environ['SINGLESTOREDB_WORKSPACE']
|
|
82
|
+
]
|
|
83
|
+
raise RuntimeError('no workspace group specified')
|
|
40
84
|
|
|
41
85
|
|
|
42
86
|
class StageObject(object):
|
|
@@ -926,6 +970,21 @@ class Workspace(object):
|
|
|
926
970
|
|
|
927
971
|
"""
|
|
928
972
|
|
|
973
|
+
name: str
|
|
974
|
+
id: str
|
|
975
|
+
group_id: str
|
|
976
|
+
size: str
|
|
977
|
+
state: str
|
|
978
|
+
created_at: Optional[datetime.datetime]
|
|
979
|
+
terminated_at: Optional[datetime.datetime]
|
|
980
|
+
endpoint: Optional[str]
|
|
981
|
+
auto_suspend: Optional[Dict[str, Any]]
|
|
982
|
+
cache_config: Optional[int]
|
|
983
|
+
deployment_type: Optional[str]
|
|
984
|
+
resume_attachments: Optional[List[Dict[str, Any]]]
|
|
985
|
+
scaling_progress: Optional[int]
|
|
986
|
+
last_resumed_at: Optional[datetime.datetime]
|
|
987
|
+
|
|
929
988
|
def __init__(
|
|
930
989
|
self,
|
|
931
990
|
name: str,
|
|
@@ -939,9 +998,9 @@ class Workspace(object):
|
|
|
939
998
|
auto_suspend: Optional[Dict[str, Any]] = None,
|
|
940
999
|
cache_config: Optional[int] = None,
|
|
941
1000
|
deployment_type: Optional[str] = None,
|
|
942
|
-
resume_attachments: Optional[Dict[str, Any]] = None,
|
|
1001
|
+
resume_attachments: Optional[List[Dict[str, Any]]] = None,
|
|
943
1002
|
scaling_progress: Optional[int] = None,
|
|
944
|
-
last_resumed_at: Optional[str] = None,
|
|
1003
|
+
last_resumed_at: Optional[Union[str, datetime.datetime]] = None,
|
|
945
1004
|
):
|
|
946
1005
|
#: Name of the workspace
|
|
947
1006
|
self.name = name
|
|
@@ -981,7 +1040,11 @@ class Workspace(object):
|
|
|
981
1040
|
self.deployment_type = deployment_type
|
|
982
1041
|
|
|
983
1042
|
#: Database attachments
|
|
984
|
-
self.resume_attachments =
|
|
1043
|
+
self.resume_attachments = [
|
|
1044
|
+
camel_to_snake_dict(x) # type: ignore
|
|
1045
|
+
for x in resume_attachments or []
|
|
1046
|
+
if x is not None
|
|
1047
|
+
]
|
|
985
1048
|
|
|
986
1049
|
#: Current progress percentage for scaling the workspace
|
|
987
1050
|
self.scaling_progress = scaling_progress
|
|
@@ -1248,8 +1311,18 @@ class WorkspaceGroup(object):
|
|
|
1248
1311
|
|
|
1249
1312
|
"""
|
|
1250
1313
|
|
|
1314
|
+
name: str
|
|
1315
|
+
id: str
|
|
1316
|
+
created_at: Optional[datetime.datetime]
|
|
1317
|
+
region: Optional[Region]
|
|
1318
|
+
firewall_ranges: List[str]
|
|
1319
|
+
terminated_at: Optional[datetime.datetime]
|
|
1320
|
+
allow_all_traffic: bool
|
|
1321
|
+
|
|
1251
1322
|
def __init__(
|
|
1252
|
-
self,
|
|
1323
|
+
self,
|
|
1324
|
+
name: str,
|
|
1325
|
+
id: str,
|
|
1253
1326
|
created_at: Union[str, datetime.datetime],
|
|
1254
1327
|
region: Optional[Region],
|
|
1255
1328
|
firewall_ranges: List[str],
|
|
@@ -1275,7 +1348,7 @@ class WorkspaceGroup(object):
|
|
|
1275
1348
|
self.terminated_at = to_datetime(terminated_at)
|
|
1276
1349
|
|
|
1277
1350
|
#: Should all traffic be allowed?
|
|
1278
|
-
self.allow_all_traffic = allow_all_traffic
|
|
1351
|
+
self.allow_all_traffic = allow_all_traffic or False
|
|
1279
1352
|
|
|
1280
1353
|
self._manager: Optional[WorkspaceManager] = None
|
|
1281
1354
|
|
|
@@ -5,12 +5,15 @@
|
|
|
5
5
|
# https://dev.mysql.com/doc/refman/5.5/en/error-handling.html
|
|
6
6
|
import errno
|
|
7
7
|
import functools
|
|
8
|
+
import io
|
|
8
9
|
import os
|
|
10
|
+
import queue
|
|
9
11
|
import socket
|
|
10
12
|
import struct
|
|
11
13
|
import sys
|
|
12
14
|
import traceback
|
|
13
15
|
import warnings
|
|
16
|
+
from typing import Iterable
|
|
14
17
|
|
|
15
18
|
try:
|
|
16
19
|
import _singlestoredb_accel
|
|
@@ -259,6 +262,8 @@ class Connection(BaseConnection):
|
|
|
259
262
|
uploading data?
|
|
260
263
|
track_env : bool, optional
|
|
261
264
|
Should the connection track the SINGLESTOREDB_URL environment variable?
|
|
265
|
+
enable_extended_data_types : bool, optional
|
|
266
|
+
Should extended data types (BSON, vector) be enabled?
|
|
262
267
|
|
|
263
268
|
See `Connection <https://www.python.org/dev/peps/pep-0249/#connection-objects>`_
|
|
264
269
|
in the specification.
|
|
@@ -328,6 +333,7 @@ class Connection(BaseConnection):
|
|
|
328
333
|
inf_as_null=None,
|
|
329
334
|
encoding_errors='strict',
|
|
330
335
|
track_env=False,
|
|
336
|
+
enable_extended_data_types=True,
|
|
331
337
|
):
|
|
332
338
|
BaseConnection.__init__(**dict(locals()))
|
|
333
339
|
|
|
@@ -350,6 +356,7 @@ class Connection(BaseConnection):
|
|
|
350
356
|
)
|
|
351
357
|
|
|
352
358
|
self._local_infile = bool(local_infile)
|
|
359
|
+
self._local_infile_stream = None
|
|
353
360
|
if self._local_infile:
|
|
354
361
|
client_flag |= CLIENT.LOCAL_FILES
|
|
355
362
|
if multi_statements:
|
|
@@ -609,6 +616,7 @@ class Connection(BaseConnection):
|
|
|
609
616
|
|
|
610
617
|
self._in_sync = False
|
|
611
618
|
self._track_env = bool(track_env) or self.host == 'singlestore.com'
|
|
619
|
+
self._enable_extended_data_types = enable_extended_data_types
|
|
612
620
|
|
|
613
621
|
if defer_connect or self._track_env:
|
|
614
622
|
self._sock = None
|
|
@@ -839,7 +847,7 @@ class Connection(BaseConnection):
|
|
|
839
847
|
return self.cursorclass(self)
|
|
840
848
|
|
|
841
849
|
# The following methods are INTERNAL USE ONLY (called from Cursor)
|
|
842
|
-
def query(self, sql, unbuffered=False):
|
|
850
|
+
def query(self, sql, unbuffered=False, infile_stream=None):
|
|
843
851
|
"""
|
|
844
852
|
Run a query on the server.
|
|
845
853
|
|
|
@@ -855,8 +863,10 @@ class Connection(BaseConnection):
|
|
|
855
863
|
else:
|
|
856
864
|
if isinstance(sql, str):
|
|
857
865
|
sql = sql.encode(self.encoding, 'surrogateescape')
|
|
866
|
+
self._local_infile_stream = infile_stream
|
|
858
867
|
self._execute_command(COMMAND.COM_QUERY, sql)
|
|
859
868
|
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
|
|
869
|
+
self._local_infile_stream = None
|
|
860
870
|
return self._affected_rows
|
|
861
871
|
|
|
862
872
|
def next_result(self, unbuffered=False):
|
|
@@ -1070,6 +1080,14 @@ class Connection(BaseConnection):
|
|
|
1070
1080
|
c.execute('SET sql_mode=%s', (self.sql_mode,))
|
|
1071
1081
|
c.close()
|
|
1072
1082
|
|
|
1083
|
+
if self._enable_extended_data_types:
|
|
1084
|
+
c = self.cursor()
|
|
1085
|
+
try:
|
|
1086
|
+
c.execute('SET @@SESSION.enable_extended_types_metadata=on')
|
|
1087
|
+
except self.OperationalError:
|
|
1088
|
+
pass
|
|
1089
|
+
c.close()
|
|
1090
|
+
|
|
1073
1091
|
if self.init_command is not None:
|
|
1074
1092
|
c = self.cursor()
|
|
1075
1093
|
c.execute(self.init_command)
|
|
@@ -1859,24 +1877,82 @@ class LoadLocalFile:
|
|
|
1859
1877
|
def send_data(self):
|
|
1860
1878
|
"""Send data packets from the local file to the server"""
|
|
1861
1879
|
if not self.connection._sock:
|
|
1862
|
-
raise err.InterfaceError(0, '')
|
|
1880
|
+
raise err.InterfaceError(0, 'Connection is closed')
|
|
1881
|
+
|
|
1863
1882
|
conn = self.connection
|
|
1883
|
+
infile = conn._local_infile_stream
|
|
1884
|
+
|
|
1885
|
+
# 16KB is efficient enough
|
|
1886
|
+
packet_size = min(conn.max_allowed_packet, 16 * 1024)
|
|
1864
1887
|
|
|
1865
1888
|
try:
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1889
|
+
|
|
1890
|
+
if self.filename in [':stream:', b':stream:']:
|
|
1891
|
+
|
|
1892
|
+
if infile is None:
|
|
1893
|
+
raise err.OperationalError(
|
|
1894
|
+
ER.FILE_NOT_FOUND,
|
|
1895
|
+
':stream: specified for LOCAL INFILE, but no stream was supplied',
|
|
1896
|
+
)
|
|
1897
|
+
|
|
1898
|
+
# Binary IO
|
|
1899
|
+
elif isinstance(infile, io.RawIOBase):
|
|
1900
|
+
while True:
|
|
1901
|
+
chunk = infile.read(packet_size)
|
|
1902
|
+
if not chunk:
|
|
1903
|
+
break
|
|
1904
|
+
conn.write_packet(chunk)
|
|
1905
|
+
|
|
1906
|
+
# Text IO
|
|
1907
|
+
elif isinstance(infile, io.TextIOBase):
|
|
1908
|
+
while True:
|
|
1909
|
+
chunk = infile.read(packet_size)
|
|
1910
|
+
if not chunk:
|
|
1911
|
+
break
|
|
1912
|
+
conn.write_packet(chunk.encode('utf8'))
|
|
1913
|
+
|
|
1914
|
+
# Iterable of bytes or str
|
|
1915
|
+
elif isinstance(infile, Iterable):
|
|
1916
|
+
for chunk in infile:
|
|
1917
|
+
if not chunk:
|
|
1918
|
+
continue
|
|
1919
|
+
if isinstance(chunk, str):
|
|
1920
|
+
conn.write_packet(chunk.encode('utf8'))
|
|
1921
|
+
else:
|
|
1922
|
+
conn.write_packet(chunk)
|
|
1923
|
+
|
|
1924
|
+
# Queue (empty value ends the iteration)
|
|
1925
|
+
elif isinstance(infile, queue.Queue):
|
|
1926
|
+
while True:
|
|
1927
|
+
chunk = infile.get()
|
|
1928
|
+
if not chunk:
|
|
1929
|
+
break
|
|
1930
|
+
if isinstance(chunk, str):
|
|
1931
|
+
conn.write_packet(chunk.encode('utf8'))
|
|
1932
|
+
else:
|
|
1933
|
+
conn.write_packet(chunk)
|
|
1934
|
+
|
|
1935
|
+
else:
|
|
1936
|
+
raise err.OperationalError(
|
|
1937
|
+
ER.FILE_NOT_FOUND,
|
|
1938
|
+
':stream: specified for LOCAL INFILE, ' +
|
|
1939
|
+
f'but stream type is unrecognized: {infile}',
|
|
1940
|
+
)
|
|
1941
|
+
|
|
1942
|
+
else:
|
|
1943
|
+
try:
|
|
1944
|
+
with open(self.filename, 'rb') as open_file:
|
|
1945
|
+
while True:
|
|
1946
|
+
chunk = open_file.read(packet_size)
|
|
1947
|
+
if not chunk:
|
|
1948
|
+
break
|
|
1949
|
+
conn.write_packet(chunk)
|
|
1950
|
+
except OSError:
|
|
1951
|
+
raise err.OperationalError(
|
|
1952
|
+
ER.FILE_NOT_FOUND,
|
|
1953
|
+
f"Can't find file '{self.filename!s}'",
|
|
1954
|
+
)
|
|
1955
|
+
|
|
1880
1956
|
finally:
|
|
1881
1957
|
if not conn._closed:
|
|
1882
1958
|
# send the empty packet to signify we are done sending data
|
|
@@ -30,3 +30,19 @@ GEOMETRY = 255
|
|
|
30
30
|
CHAR = TINY
|
|
31
31
|
INTERVAL = ENUM
|
|
32
32
|
BOOL = TINY
|
|
33
|
+
|
|
34
|
+
# SingleStoreDB-specific.
|
|
35
|
+
# Only enabled when enable_extended_types_metadata=1 in the server.
|
|
36
|
+
BSON = 1001
|
|
37
|
+
FLOAT32_VECTOR_JSON = 2001
|
|
38
|
+
FLOAT64_VECTOR_JSON = 2002
|
|
39
|
+
INT8_VECTOR_JSON = 2003
|
|
40
|
+
INT16_VECTOR_JSON = 2004
|
|
41
|
+
INT32_VECTOR_JSON = 2005
|
|
42
|
+
INT64_VECTOR_JSON = 2006
|
|
43
|
+
FLOAT32_VECTOR = 3001
|
|
44
|
+
FLOAT64_VECTOR = 3002
|
|
45
|
+
INT8_VECTOR = 3003
|
|
46
|
+
INT16_VECTOR = 3004
|
|
47
|
+
INT32_VECTOR = 3005
|
|
48
|
+
INT64_VECTOR = 3006
|
singlestoredb/mysql/cursors.py
CHANGED
|
@@ -178,7 +178,7 @@ class Cursor(BaseCursor):
|
|
|
178
178
|
|
|
179
179
|
return query
|
|
180
180
|
|
|
181
|
-
def execute(self, query, args=None):
|
|
181
|
+
def execute(self, query, args=None, infile_stream=None):
|
|
182
182
|
"""
|
|
183
183
|
Execute a query.
|
|
184
184
|
|
|
@@ -192,6 +192,8 @@ class Cursor(BaseCursor):
|
|
|
192
192
|
Query to execute.
|
|
193
193
|
args : Sequence[Any] or Dict[str, Any] or Any, optional
|
|
194
194
|
Parameters used with query. (optional)
|
|
195
|
+
infile_stream : io.BytesIO or Iterator[bytes], optional
|
|
196
|
+
Data stream for ``LOCAL INFILE`` statements
|
|
195
197
|
|
|
196
198
|
Returns
|
|
197
199
|
-------
|
|
@@ -205,7 +207,7 @@ class Cursor(BaseCursor):
|
|
|
205
207
|
|
|
206
208
|
query = self.mogrify(query, args)
|
|
207
209
|
|
|
208
|
-
result = self._query(query)
|
|
210
|
+
result = self._query(query, infile_stream=infile_stream)
|
|
209
211
|
self._executed = query
|
|
210
212
|
return result
|
|
211
213
|
|
|
@@ -387,10 +389,10 @@ class Cursor(BaseCursor):
|
|
|
387
389
|
raise IndexError('out of range')
|
|
388
390
|
self._rownumber = r
|
|
389
391
|
|
|
390
|
-
def _query(self, q):
|
|
392
|
+
def _query(self, q, infile_stream=None):
|
|
391
393
|
conn = self._get_db()
|
|
392
394
|
self._clear_result()
|
|
393
|
-
conn.query(q)
|
|
395
|
+
conn.query(q, infile_stream=infile_stream)
|
|
394
396
|
self._do_get_result()
|
|
395
397
|
return self.rowcount
|
|
396
398
|
|
|
@@ -417,10 +419,11 @@ class Cursor(BaseCursor):
|
|
|
417
419
|
if self.rowcount == 18446744073709551615:
|
|
418
420
|
self.rowcount = -1
|
|
419
421
|
self._description = result.description
|
|
420
|
-
self.
|
|
421
|
-
self.
|
|
422
|
-
|
|
423
|
-
|
|
422
|
+
if self._description:
|
|
423
|
+
self._format_schema = get_schema(
|
|
424
|
+
self.connection._results_type,
|
|
425
|
+
result.description,
|
|
426
|
+
)
|
|
424
427
|
self.lastrowid = result.insert_id
|
|
425
428
|
self._rows = result.rows
|
|
426
429
|
|
|
@@ -679,10 +682,10 @@ class SSCursor(Cursor):
|
|
|
679
682
|
|
|
680
683
|
__del__ = close
|
|
681
684
|
|
|
682
|
-
def _query(self, q):
|
|
685
|
+
def _query(self, q, infile_stream=None):
|
|
683
686
|
conn = self._get_db()
|
|
684
687
|
self._clear_result()
|
|
685
|
-
conn.query(q, unbuffered=True)
|
|
688
|
+
conn.query(q, unbuffered=True, infile_stream=infile_stream)
|
|
686
689
|
self._do_get_result()
|
|
687
690
|
return self.rowcount
|
|
688
691
|
|
singlestoredb/mysql/protocol.py
CHANGED
|
@@ -8,8 +8,10 @@ from . import err
|
|
|
8
8
|
from ..config import get_option
|
|
9
9
|
from ..utils.results import Description
|
|
10
10
|
from .charset import MBLENGTH
|
|
11
|
+
from .constants import EXTENDED_TYPE
|
|
11
12
|
from .constants import FIELD_TYPE
|
|
12
13
|
from .constants import SERVER_STATUS
|
|
14
|
+
from .constants import VECTOR_TYPE
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
DEBUG = get_option('debug.connection')
|
|
@@ -264,16 +266,63 @@ class FieldDescriptorPacket(MysqlPacket):
|
|
|
264
266
|
self.org_table = self.read_length_coded_string().decode(encoding)
|
|
265
267
|
self.name = self.read_length_coded_string().decode(encoding)
|
|
266
268
|
self.org_name = self.read_length_coded_string().decode(encoding)
|
|
269
|
+
n_bytes = 0
|
|
267
270
|
(
|
|
271
|
+
n_bytes,
|
|
268
272
|
self.charsetnr,
|
|
269
273
|
self.length,
|
|
270
274
|
self.type_code,
|
|
271
275
|
self.flags,
|
|
272
276
|
self.scale,
|
|
273
|
-
) = self.read_struct('<
|
|
277
|
+
) = self.read_struct('<BHIBHBxx')
|
|
278
|
+
|
|
274
279
|
# 'default' is a length coded binary and is still in the buffer?
|
|
275
280
|
# not used for normal result sets...
|
|
276
281
|
|
|
282
|
+
# Extended types
|
|
283
|
+
if n_bytes > 12:
|
|
284
|
+
ext_type_code = self.read_uint8()
|
|
285
|
+
if ext_type_code == EXTENDED_TYPE.NONE:
|
|
286
|
+
pass
|
|
287
|
+
elif ext_type_code == EXTENDED_TYPE.BSON:
|
|
288
|
+
self.type_code = FIELD_TYPE.BSON
|
|
289
|
+
elif ext_type_code == EXTENDED_TYPE.VECTOR:
|
|
290
|
+
(self.length, vec_type) = self.read_struct('<IB')
|
|
291
|
+
if vec_type == VECTOR_TYPE.FLOAT32:
|
|
292
|
+
if self.charsetnr == 63:
|
|
293
|
+
self.type_code = FIELD_TYPE.FLOAT32_VECTOR
|
|
294
|
+
else:
|
|
295
|
+
self.type_code = FIELD_TYPE.FLOAT32_VECTOR_JSON
|
|
296
|
+
elif vec_type == VECTOR_TYPE.FLOAT64:
|
|
297
|
+
if self.charsetnr == 63:
|
|
298
|
+
self.type_code = FIELD_TYPE.FLOAT64_VECTOR
|
|
299
|
+
else:
|
|
300
|
+
self.type_code = FIELD_TYPE.FLOAT64_VECTOR_JSON
|
|
301
|
+
elif vec_type == VECTOR_TYPE.INT8:
|
|
302
|
+
if self.charsetnr == 63:
|
|
303
|
+
self.type_code = FIELD_TYPE.INT8_VECTOR
|
|
304
|
+
else:
|
|
305
|
+
self.type_code = FIELD_TYPE.INT8_VECTOR_JSON
|
|
306
|
+
elif vec_type == VECTOR_TYPE.INT16:
|
|
307
|
+
if self.charsetnr == 63:
|
|
308
|
+
self.type_code = FIELD_TYPE.INT16_VECTOR
|
|
309
|
+
else:
|
|
310
|
+
self.type_code = FIELD_TYPE.INT16_VECTOR_JSON
|
|
311
|
+
elif vec_type == VECTOR_TYPE.INT32:
|
|
312
|
+
if self.charsetnr == 63:
|
|
313
|
+
self.type_code = FIELD_TYPE.INT32_VECTOR
|
|
314
|
+
else:
|
|
315
|
+
self.type_code = FIELD_TYPE.INT32_VECTOR_JSON
|
|
316
|
+
elif vec_type == VECTOR_TYPE.INT64:
|
|
317
|
+
if self.charsetnr == 63:
|
|
318
|
+
self.type_code = FIELD_TYPE.INT64_VECTOR
|
|
319
|
+
else:
|
|
320
|
+
self.type_code = FIELD_TYPE.INT64_VECTOR_JSON
|
|
321
|
+
else:
|
|
322
|
+
raise TypeError(f'unrecognized vector data type: {vec_type}')
|
|
323
|
+
else:
|
|
324
|
+
raise TypeError(f'unrecognized extended data type: {ext_type_code}')
|
|
325
|
+
|
|
277
326
|
def description(self):
|
|
278
327
|
"""Provides a 7-item tuple compatible with the Python PEP249 DB Spec."""
|
|
279
328
|
return Description(
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
|
|
11
|
+
if 'SINGLESTOREDB_ORGANIZATION' not in _os.environ:
|
|
12
|
+
_warnings.warn(
|
|
13
|
+
'This package is intended for use in the SingleStoreDB notebook environment',
|
|
14
|
+
RuntimeWarning,
|
|
15
|
+
)
|