singlestoredb 0.4.0__py3-none-any.whl → 1.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of singlestoredb might be problematic. Click here for more details.
- singlestoredb/__init__.py +33 -1
- singlestoredb/alchemy/__init__.py +90 -0
- singlestoredb/auth.py +5 -1
- singlestoredb/config.py +116 -14
- singlestoredb/connection.py +483 -516
- singlestoredb/converters.py +238 -135
- singlestoredb/exceptions.py +30 -2
- singlestoredb/functions/__init__.py +1 -0
- singlestoredb/functions/decorator.py +142 -0
- singlestoredb/functions/dtypes.py +1639 -0
- singlestoredb/functions/ext/__init__.py +2 -0
- singlestoredb/functions/ext/arrow.py +375 -0
- singlestoredb/functions/ext/asgi.py +661 -0
- singlestoredb/functions/ext/json.py +427 -0
- singlestoredb/functions/ext/mmap.py +306 -0
- singlestoredb/functions/ext/rowdat_1.py +744 -0
- singlestoredb/functions/signature.py +673 -0
- singlestoredb/fusion/__init__.py +11 -0
- singlestoredb/fusion/graphql.py +213 -0
- singlestoredb/fusion/handler.py +621 -0
- singlestoredb/fusion/handlers/stage.py +257 -0
- singlestoredb/fusion/handlers/utils.py +162 -0
- singlestoredb/fusion/handlers/workspace.py +412 -0
- singlestoredb/fusion/registry.py +164 -0
- singlestoredb/fusion/result.py +399 -0
- singlestoredb/http/__init__.py +27 -0
- singlestoredb/{http.py → http/connection.py} +555 -154
- singlestoredb/management/__init__.py +3 -0
- singlestoredb/management/billing_usage.py +148 -0
- singlestoredb/management/cluster.py +14 -6
- singlestoredb/management/manager.py +100 -38
- singlestoredb/management/organization.py +188 -0
- singlestoredb/management/region.py +5 -5
- singlestoredb/management/utils.py +281 -2
- singlestoredb/management/workspace.py +1344 -49
- singlestoredb/{clients/pymysqlsv → mysql}/__init__.py +16 -21
- singlestoredb/{clients/pymysqlsv → mysql}/_auth.py +39 -8
- singlestoredb/{clients/pymysqlsv → mysql}/charset.py +26 -23
- singlestoredb/{clients/pymysqlsv/connections.py → mysql/connection.py} +532 -165
- singlestoredb/{clients/pymysqlsv → mysql}/constants/CLIENT.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/COMMAND.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/CR.py +0 -2
- singlestoredb/{clients/pymysqlsv → mysql}/constants/ER.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/FIELD_TYPE.py +1 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/FLAG.py +0 -1
- singlestoredb/{clients/pymysqlsv → mysql}/constants/SERVER_STATUS.py +0 -1
- singlestoredb/mysql/converters.py +271 -0
- singlestoredb/{clients/pymysqlsv → mysql}/cursors.py +228 -112
- singlestoredb/mysql/err.py +92 -0
- singlestoredb/{clients/pymysqlsv → mysql}/optionfile.py +5 -4
- singlestoredb/{clients/pymysqlsv → mysql}/protocol.py +49 -20
- singlestoredb/mysql/tests/__init__.py +19 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/base.py +32 -12
- singlestoredb/mysql/tests/conftest.py +37 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_DictCursor.py +11 -7
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_SSCursor.py +17 -12
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_basic.py +32 -24
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_connection.py +130 -119
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_converters.py +9 -7
- singlestoredb/mysql/tests/test_cursor.py +141 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_err.py +3 -2
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_issues.py +35 -27
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_load_local.py +13 -11
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_nextset.py +7 -3
- singlestoredb/{clients/pymysqlsv → mysql}/tests/test_optionfile.py +2 -1
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/__init__.py +1 -1
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/capabilities.py +19 -17
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/dbapi20.py +31 -22
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +3 -4
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +24 -20
- singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +4 -4
- singlestoredb/{clients/pymysqlsv → mysql}/times.py +3 -4
- singlestoredb/pytest.py +283 -0
- singlestoredb/tests/empty.sql +0 -0
- singlestoredb/tests/ext_funcs/__init__.py +385 -0
- singlestoredb/tests/test.sql +210 -0
- singlestoredb/tests/test2.sql +1 -0
- singlestoredb/tests/test_basics.py +482 -115
- singlestoredb/tests/test_config.py +13 -13
- singlestoredb/tests/test_connection.py +241 -305
- singlestoredb/tests/test_dbapi.py +27 -0
- singlestoredb/tests/test_ext_func.py +1193 -0
- singlestoredb/tests/test_ext_func_data.py +1101 -0
- singlestoredb/tests/test_fusion.py +465 -0
- singlestoredb/tests/test_http.py +32 -26
- singlestoredb/tests/test_management.py +588 -8
- singlestoredb/tests/test_plugin.py +33 -0
- singlestoredb/tests/test_results.py +11 -12
- singlestoredb/tests/test_udf.py +687 -0
- singlestoredb/tests/utils.py +3 -2
- singlestoredb/utils/config.py +58 -0
- singlestoredb/utils/debug.py +13 -0
- singlestoredb/utils/mogrify.py +151 -0
- singlestoredb/utils/results.py +4 -1
- singlestoredb-1.0.4.dist-info/METADATA +139 -0
- singlestoredb-1.0.4.dist-info/RECORD +112 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/WHEEL +1 -1
- singlestoredb-1.0.4.dist-info/entry_points.txt +2 -0
- singlestoredb/clients/pymysqlsv/converters.py +0 -365
- singlestoredb/clients/pymysqlsv/err.py +0 -144
- singlestoredb/clients/pymysqlsv/tests/__init__.py +0 -19
- singlestoredb/clients/pymysqlsv/tests/test_cursor.py +0 -133
- singlestoredb/clients/pymysqlsv/tests/thirdparty/test_MySQLdb/__init__.py +0 -9
- singlestoredb/drivers/__init__.py +0 -45
- singlestoredb/drivers/base.py +0 -198
- singlestoredb/drivers/cymysql.py +0 -38
- singlestoredb/drivers/http.py +0 -47
- singlestoredb/drivers/mariadb.py +0 -40
- singlestoredb/drivers/mysqlconnector.py +0 -49
- singlestoredb/drivers/mysqldb.py +0 -60
- singlestoredb/drivers/pymysql.py +0 -37
- singlestoredb/drivers/pymysqlsv.py +0 -35
- singlestoredb/drivers/pyodbc.py +0 -65
- singlestoredb-0.4.0.dist-info/METADATA +0 -111
- singlestoredb-0.4.0.dist-info/RECORD +0 -86
- /singlestoredb/{clients → fusion/handlers}/__init__.py +0 -0
- /singlestoredb/{clients/pymysqlsv → mysql}/constants/__init__.py +0 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/LICENSE +0 -0
- {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import time
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from typing import Any
|
|
5
|
+
from typing import Callable
|
|
6
|
+
from typing import Dict
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
11
|
+
from ..converters import converters as decoders
|
|
12
|
+
from .err import ProgrammingError
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
import numpy as np
|
|
16
|
+
has_numpy = True
|
|
17
|
+
except ImportError:
|
|
18
|
+
has_numpy = False
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
import shapely.geometry
|
|
22
|
+
import shapely.wkt
|
|
23
|
+
has_shapely = True
|
|
24
|
+
except ImportError:
|
|
25
|
+
has_shapely = False
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
import pygeos
|
|
29
|
+
has_pygeos = True
|
|
30
|
+
except ImportError:
|
|
31
|
+
has_pygeos = False
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
Encoders = Dict[type, Callable[..., Union[str, Dict[str, str]]]]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def escape_item(val: Any, charset: str, mapping: Optional[Encoders] = None) -> str:
|
|
38
|
+
if mapping is None:
|
|
39
|
+
mapping = encoders
|
|
40
|
+
encoder = mapping.get(type(val), None)
|
|
41
|
+
|
|
42
|
+
# Fallback to default when no encoder found
|
|
43
|
+
if encoder is None:
|
|
44
|
+
try:
|
|
45
|
+
encoder = mapping[str]
|
|
46
|
+
except KeyError:
|
|
47
|
+
raise TypeError('no default type converter defined')
|
|
48
|
+
|
|
49
|
+
if encoder in (escape_dict, escape_sequence):
|
|
50
|
+
val = encoder(val, charset, mapping)
|
|
51
|
+
else:
|
|
52
|
+
val = encoder(val, mapping)
|
|
53
|
+
return val
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def escape_dict(
|
|
57
|
+
val: Dict[str, Any],
|
|
58
|
+
charset: str,
|
|
59
|
+
mapping: Optional[Encoders] = None,
|
|
60
|
+
) -> Dict[str, str]:
|
|
61
|
+
n = {}
|
|
62
|
+
for k, v in val.items():
|
|
63
|
+
quoted = escape_item(v, charset, mapping)
|
|
64
|
+
n[k] = quoted
|
|
65
|
+
return n
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def escape_sequence(
|
|
69
|
+
val: Any,
|
|
70
|
+
charset: str,
|
|
71
|
+
mapping: Optional[Encoders] = None,
|
|
72
|
+
) -> str:
|
|
73
|
+
n = []
|
|
74
|
+
for item in val:
|
|
75
|
+
quoted = escape_item(item, charset, mapping)
|
|
76
|
+
n.append(quoted)
|
|
77
|
+
return '(' + ','.join(n) + ')'
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def escape_set(val: Any, charset: str, mapping: Optional[Encoders] = None) -> str:
|
|
81
|
+
return ','.join([escape_item(x, charset, mapping) for x in val])
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def escape_bool(value: Any, mapping: Optional[Encoders] = None) -> str:
|
|
85
|
+
return str(int(value))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def escape_int(value: Any, mapping: Optional[Encoders] = None) -> str:
|
|
89
|
+
return str(value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def escape_float(
|
|
93
|
+
value: Any,
|
|
94
|
+
mapping: Optional[Encoders] = None,
|
|
95
|
+
nan_as_null: bool = False,
|
|
96
|
+
inf_as_null: bool = False,
|
|
97
|
+
) -> str:
|
|
98
|
+
s = repr(value)
|
|
99
|
+
if s == 'nan':
|
|
100
|
+
if nan_as_null:
|
|
101
|
+
return 'NULL'
|
|
102
|
+
raise ProgrammingError(0, '%s can not be used with SingleStoreDB' % s)
|
|
103
|
+
if s == 'inf':
|
|
104
|
+
if inf_as_null:
|
|
105
|
+
return 'NULL'
|
|
106
|
+
raise ProgrammingError(0, '%s can not be used with SingleStoreDB' % s)
|
|
107
|
+
if 'e' not in s:
|
|
108
|
+
s += 'e0'
|
|
109
|
+
return s
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
_escape_table = [chr(x) for x in range(128)]
|
|
113
|
+
_escape_table[0] = '\\0'
|
|
114
|
+
_escape_table[ord('\\')] = '\\\\'
|
|
115
|
+
_escape_table[ord('\n')] = '\\n'
|
|
116
|
+
_escape_table[ord('\r')] = '\\r'
|
|
117
|
+
_escape_table[ord('\032')] = '\\Z'
|
|
118
|
+
_escape_table[ord('"')] = '\\"'
|
|
119
|
+
_escape_table[ord("'")] = "\\'"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def escape_string(value: str, mapping: Optional[Encoders] = None) -> str:
|
|
123
|
+
"""
|
|
124
|
+
Escapes *value* without adding quote.
|
|
125
|
+
|
|
126
|
+
Value should be unicode
|
|
127
|
+
|
|
128
|
+
"""
|
|
129
|
+
return value.translate(_escape_table)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def escape_bytes_prefixed(value: bytes, mapping: Optional[Encoders] = None) -> str:
|
|
133
|
+
return "_binary X'{}'".format(value.hex())
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def escape_bytes(value: bytes, mapping: Optional[Encoders] = None) -> str:
|
|
137
|
+
return "X'{}'".format(value.hex())
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def escape_str(value: str, mapping: Optional[Encoders] = None) -> str:
|
|
141
|
+
return "'{}'".format(escape_string(str(value), mapping))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def escape_None(value: str, mapping: Optional[Encoders] = None) -> str:
|
|
145
|
+
return 'NULL'
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def escape_timedelta(obj: datetime.timedelta, mapping: Optional[Encoders] = None) -> str:
|
|
149
|
+
seconds = int(obj.seconds) % 60
|
|
150
|
+
minutes = int(obj.seconds // 60) % 60
|
|
151
|
+
hours = int(obj.seconds // 3600) % 24 + int(obj.days) * 24
|
|
152
|
+
if obj.microseconds:
|
|
153
|
+
fmt = "'{0:02d}:{1:02d}:{2:02d}.{3:06d}'"
|
|
154
|
+
else:
|
|
155
|
+
fmt = "'{0:02d}:{1:02d}:{2:02d}'"
|
|
156
|
+
return fmt.format(hours, minutes, seconds, obj.microseconds)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def escape_time(obj: datetime.time, mapping: Optional[Encoders] = None) -> str:
|
|
160
|
+
if obj.microsecond:
|
|
161
|
+
fmt = "'{0.hour:02}:{0.minute:02}:{0.second:02}.{0.microsecond:06}'"
|
|
162
|
+
else:
|
|
163
|
+
fmt = "'{0.hour:02}:{0.minute:02}:{0.second:02}'"
|
|
164
|
+
return fmt.format(obj)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def escape_datetime(obj: datetime.datetime, mapping: Optional[Encoders] = None) -> str:
|
|
168
|
+
if obj.microsecond:
|
|
169
|
+
fmt = "'{0.year:04}-{0.month:02}-{0.day:02} " \
|
|
170
|
+
"{0.hour:02}:{0.minute:02}:{0.second:02}.{0.microsecond:06}'"
|
|
171
|
+
else:
|
|
172
|
+
fmt = "'{0.year:04}-{0.month:02}-{0.day:02} " \
|
|
173
|
+
"{0.hour:02}:{0.minute:02}:{0.second:02}'"
|
|
174
|
+
return fmt.format(obj)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def escape_date(obj: datetime.date, mapping: Optional[Encoders] = None) -> str:
|
|
178
|
+
fmt = "'{0.year:04}-{0.month:02}-{0.day:02}'"
|
|
179
|
+
return fmt.format(obj)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def escape_struct_time(obj: Tuple[Any, ...], mapping: Optional[Encoders] = None) -> str:
|
|
183
|
+
return escape_datetime(datetime.datetime(*obj[:6]))
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def Decimal2Literal(o: Any, d: Any) -> str:
|
|
187
|
+
return format(o, 'f')
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def through(x: Any) -> Any:
|
|
191
|
+
return x
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# def convert_bit(b):
|
|
195
|
+
# b = "\x00" * (8 - len(b)) + b # pad w/ zeroes
|
|
196
|
+
# return struct.unpack(">Q", b)[0]
|
|
197
|
+
#
|
|
198
|
+
# the snippet above is right, but MySQLdb doesn't process bits,
|
|
199
|
+
# so we shouldn't either
|
|
200
|
+
convert_bit = through
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
encoders: Encoders = {
|
|
204
|
+
bool: escape_bool,
|
|
205
|
+
int: escape_int,
|
|
206
|
+
float: escape_float,
|
|
207
|
+
str: escape_str,
|
|
208
|
+
bytes: escape_bytes,
|
|
209
|
+
tuple: escape_sequence,
|
|
210
|
+
list: escape_sequence,
|
|
211
|
+
set: escape_sequence,
|
|
212
|
+
frozenset: escape_sequence,
|
|
213
|
+
dict: escape_dict,
|
|
214
|
+
type(None): escape_None,
|
|
215
|
+
datetime.date: escape_date,
|
|
216
|
+
datetime.datetime: escape_datetime,
|
|
217
|
+
datetime.timedelta: escape_timedelta,
|
|
218
|
+
datetime.time: escape_time,
|
|
219
|
+
time.struct_time: escape_struct_time,
|
|
220
|
+
Decimal: Decimal2Literal,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if has_numpy:
|
|
224
|
+
|
|
225
|
+
def escape_numpy(value: Any, mapping: Optional[Encoders] = None) -> str:
|
|
226
|
+
"""Convert numpy arrays to vectors of bytes."""
|
|
227
|
+
return escape_bytes(value.tobytes(), mapping=mapping)
|
|
228
|
+
|
|
229
|
+
encoders[np.ndarray] = escape_numpy
|
|
230
|
+
encoders[np.float16] = escape_float
|
|
231
|
+
encoders[np.float32] = escape_float
|
|
232
|
+
encoders[np.float64] = escape_float
|
|
233
|
+
if hasattr(np, 'float128'):
|
|
234
|
+
encoders[np.float128] = escape_float
|
|
235
|
+
encoders[np.uint] = escape_int
|
|
236
|
+
encoders[np.uint8] = escape_int
|
|
237
|
+
encoders[np.uint16] = escape_int
|
|
238
|
+
encoders[np.uint32] = escape_int
|
|
239
|
+
encoders[np.uint64] = escape_int
|
|
240
|
+
encoders[np.integer] = escape_int
|
|
241
|
+
encoders[np.int_] = escape_int
|
|
242
|
+
encoders[np.int8] = escape_int
|
|
243
|
+
encoders[np.int16] = escape_int
|
|
244
|
+
encoders[np.int32] = escape_int
|
|
245
|
+
encoders[np.int64] = escape_int
|
|
246
|
+
|
|
247
|
+
if has_shapely:
|
|
248
|
+
|
|
249
|
+
def escape_shapely(value: Any, mapping: Optional[Encoders] = None) -> str:
|
|
250
|
+
"""Convert shapely geo objects."""
|
|
251
|
+
return escape_str(shapely.wkt.dumps(value), mapping=mapping)
|
|
252
|
+
|
|
253
|
+
encoders[shapely.geometry.Polygon] = escape_shapely
|
|
254
|
+
encoders[shapely.geometry.Point] = escape_shapely
|
|
255
|
+
encoders[shapely.geometry.LineString] = escape_shapely
|
|
256
|
+
|
|
257
|
+
if has_pygeos:
|
|
258
|
+
|
|
259
|
+
def escape_pygeos(value: Any, mapping: Optional[Encoders] = None) -> str:
|
|
260
|
+
"""Convert pygeos objects."""
|
|
261
|
+
return escape_str(pygeos.io.to_wkt(value), mapping=mapping)
|
|
262
|
+
|
|
263
|
+
encoders[pygeos.Geometry] = escape_pygeos
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# for MySQLdb compatibility
|
|
267
|
+
conversions = encoders.copy() # type: ignore
|
|
268
|
+
conversions.update(decoders) # type: ignore
|
|
269
|
+
Thing2Literal = escape_str
|
|
270
|
+
|
|
271
|
+
# Run doctests with `pytest --doctest-modules pymysql/converters.py`
|