singlestoredb 1.0.3__cp38-abi3-win_amd64.whl → 1.1.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 +1 -1
- singlestoredb/config.py +125 -0
- singlestoredb/functions/dtypes.py +5 -198
- singlestoredb/functions/ext/__init__.py +0 -1
- singlestoredb/functions/ext/asgi.py +665 -153
- singlestoredb/functions/ext/json.py +2 -2
- singlestoredb/functions/ext/mmap.py +174 -67
- singlestoredb/functions/ext/rowdat_1.py +2 -2
- singlestoredb/functions/ext/utils.py +169 -0
- singlestoredb/fusion/handler.py +109 -9
- singlestoredb/fusion/handlers/stage.py +150 -0
- singlestoredb/fusion/handlers/workspace.py +265 -4
- singlestoredb/fusion/registry.py +69 -1
- singlestoredb/http/connection.py +40 -2
- singlestoredb/management/utils.py +30 -0
- singlestoredb/management/workspace.py +209 -35
- singlestoredb/mysql/connection.py +69 -0
- singlestoredb/mysql/cursors.py +176 -4
- singlestoredb/tests/test.sql +210 -0
- singlestoredb/tests/test_connection.py +1408 -0
- singlestoredb/tests/test_ext_func.py +2 -2
- singlestoredb/tests/test_ext_func_data.py +1 -1
- singlestoredb/utils/dtypes.py +205 -0
- singlestoredb/utils/results.py +367 -14
- {singlestoredb-1.0.3.dist-info → singlestoredb-1.1.0.dist-info}/METADATA +2 -1
- {singlestoredb-1.0.3.dist-info → singlestoredb-1.1.0.dist-info}/RECORD +31 -29
- {singlestoredb-1.0.3.dist-info → singlestoredb-1.1.0.dist-info}/LICENSE +0 -0
- {singlestoredb-1.0.3.dist-info → singlestoredb-1.1.0.dist-info}/WHEEL +0 -0
- {singlestoredb-1.0.3.dist-info → singlestoredb-1.1.0.dist-info}/entry_points.txt +0 -0
- {singlestoredb-1.0.3.dist-info → singlestoredb-1.1.0.dist-info}/top_level.txt +0 -0
singlestoredb/fusion/handler.py
CHANGED
|
@@ -45,6 +45,8 @@ BUILTINS = {
|
|
|
45
45
|
'<limit>': r'''
|
|
46
46
|
limit = LIMIT <integer>
|
|
47
47
|
''',
|
|
48
|
+
'<integer>': '',
|
|
49
|
+
'<number>': '',
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
BUILTIN_DEFAULTS = { # type: ignore
|
|
@@ -170,8 +172,8 @@ def build_cmd(grammar: str) -> str:
|
|
|
170
172
|
return f'{space}{cmd} ={begin}\n{end}'
|
|
171
173
|
|
|
172
174
|
|
|
173
|
-
def
|
|
174
|
-
"""Construct full
|
|
175
|
+
def build_syntax(grammar: str) -> str:
|
|
176
|
+
"""Construct full syntax."""
|
|
175
177
|
if ';' not in grammar:
|
|
176
178
|
raise ValueError('a semi-colon exist at the end of the primary rule')
|
|
177
179
|
|
|
@@ -199,8 +201,96 @@ def build_help(grammar: str) -> str:
|
|
|
199
201
|
return cmd
|
|
200
202
|
|
|
201
203
|
|
|
204
|
+
def _format_examples(ex: str) -> str:
|
|
205
|
+
"""Convert examples into sections."""
|
|
206
|
+
return re.sub(r'(^Example\s+\d+.*$)', r'### \1', ex, flags=re.M)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _format_arguments(arg: str) -> str:
|
|
210
|
+
"""Format arguments as subsections."""
|
|
211
|
+
out = []
|
|
212
|
+
for line in arg.split('\n'):
|
|
213
|
+
if line.startswith('<'):
|
|
214
|
+
out.append(f'### {line.replace("<", "<").replace(">", ">")}')
|
|
215
|
+
out.append('')
|
|
216
|
+
else:
|
|
217
|
+
out.append(line.strip())
|
|
218
|
+
return '\n'.join(out)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _to_markdown(txt: str) -> str:
|
|
222
|
+
"""Convert formatting to markdown."""
|
|
223
|
+
txt = txt.replace('``', '`')
|
|
224
|
+
|
|
225
|
+
# Format code blocks
|
|
226
|
+
lines = re.split(r'\n', txt)
|
|
227
|
+
out = []
|
|
228
|
+
while lines:
|
|
229
|
+
line = lines.pop(0)
|
|
230
|
+
if line.endswith('::'):
|
|
231
|
+
out.append(line[:-2])
|
|
232
|
+
code = []
|
|
233
|
+
while lines and (not lines[0].strip() or lines[0].startswith(' ')):
|
|
234
|
+
code.append(lines.pop(0).rstrip())
|
|
235
|
+
out.extend(['```sql', '\n'.join(code).rstrip(), '```\n'])
|
|
236
|
+
else:
|
|
237
|
+
out.append(line)
|
|
238
|
+
|
|
239
|
+
return '\n'.join(out)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def build_help(syntax: str, grammar: str) -> str:
|
|
243
|
+
"""Build full help text."""
|
|
244
|
+
syntax = re.sub(r'\s*\n+\s*', r' ', syntax.strip())
|
|
245
|
+
|
|
246
|
+
cmd = re.match(r'([A-Z0-9_ ]+)', syntax)
|
|
247
|
+
if not cmd:
|
|
248
|
+
raise ValueError(f'no command found: {syntax}')
|
|
249
|
+
|
|
250
|
+
out = [f'# {cmd.group(1)}\n\n']
|
|
251
|
+
|
|
252
|
+
sections: Dict[str, str] = {}
|
|
253
|
+
grammar = textwrap.dedent(grammar.rstrip())
|
|
254
|
+
desc_re = re.compile(r'^([A-Z][\S ]+)\s*^\-\-\-\-+\s*$', flags=re.M)
|
|
255
|
+
if desc_re.search(grammar):
|
|
256
|
+
_, *txt = desc_re.split(grammar)
|
|
257
|
+
txt = [x.strip() for x in txt]
|
|
258
|
+
sections = {}
|
|
259
|
+
while txt:
|
|
260
|
+
key = txt.pop(0)
|
|
261
|
+
value = txt.pop(0)
|
|
262
|
+
sections[key.lower()] = _to_markdown(value).strip()
|
|
263
|
+
|
|
264
|
+
if 'description' in sections:
|
|
265
|
+
out.extend([sections['description'], '\n\n'])
|
|
266
|
+
|
|
267
|
+
out.extend(['## Syntax\n```sql\n', syntax, '\n```\n\n'])
|
|
268
|
+
|
|
269
|
+
if 'arguments' in sections:
|
|
270
|
+
out.extend([
|
|
271
|
+
'## Arguments\n\n',
|
|
272
|
+
_format_arguments(sections['arguments']),
|
|
273
|
+
'\n\n',
|
|
274
|
+
])
|
|
275
|
+
|
|
276
|
+
if 'remarks' in sections:
|
|
277
|
+
out.extend(['## Remarks\n', sections['remarks'], '\n\n'])
|
|
278
|
+
|
|
279
|
+
if 'examples' in sections:
|
|
280
|
+
out.extend(['## Examples\n\n', _format_examples(sections['examples']), '\n\n'])
|
|
281
|
+
elif 'example' in sections:
|
|
282
|
+
out.extend(['## Example\n\n', _format_examples(sections['example']), '\n\n'])
|
|
283
|
+
|
|
284
|
+
if 'see also' in sections:
|
|
285
|
+
out.extend(['## See Also\n', sections['see also'], '\n\n'])
|
|
286
|
+
|
|
287
|
+
return ''.join(out).rstrip() + '\n'
|
|
288
|
+
|
|
289
|
+
|
|
202
290
|
def strip_comments(grammar: str) -> str:
|
|
203
291
|
"""Strip comments from grammar."""
|
|
292
|
+
desc_re = re.compile(r'(^\s*Description\s*^\s*-----------\s*$)', flags=re.M)
|
|
293
|
+
grammar = desc_re.split(grammar, maxsplit=1)[0]
|
|
204
294
|
return re.sub(r'^\s*#.*$', r'', grammar, flags=re.M)
|
|
205
295
|
|
|
206
296
|
|
|
@@ -226,7 +316,9 @@ def inject_builtins(grammar: str) -> str:
|
|
|
226
316
|
return grammar
|
|
227
317
|
|
|
228
318
|
|
|
229
|
-
def process_grammar(
|
|
319
|
+
def process_grammar(
|
|
320
|
+
grammar: str,
|
|
321
|
+
) -> Tuple[Grammar, Tuple[str, ...], Dict[str, Any], str, str]:
|
|
230
322
|
"""
|
|
231
323
|
Convert SQL grammar to a Parsimonious grammar.
|
|
232
324
|
|
|
@@ -247,10 +339,12 @@ def process_grammar(grammar: str) -> Tuple[Grammar, Tuple[str, ...], Dict[str, A
|
|
|
247
339
|
rules = {}
|
|
248
340
|
rule_info = {}
|
|
249
341
|
|
|
342
|
+
full_grammar = grammar
|
|
250
343
|
grammar = strip_comments(grammar)
|
|
251
344
|
grammar = inject_builtins(grammar)
|
|
252
345
|
command_key = get_keywords(grammar)
|
|
253
|
-
|
|
346
|
+
syntax_txt = build_syntax(grammar)
|
|
347
|
+
help_txt = build_help(syntax_txt, full_grammar)
|
|
254
348
|
grammar = build_cmd(grammar)
|
|
255
349
|
|
|
256
350
|
# Make sure grouping characters all have whitespace around them
|
|
@@ -336,7 +430,10 @@ def process_grammar(grammar: str) -> Tuple[Grammar, Tuple[str, ...], Dict[str, A
|
|
|
336
430
|
cmds = ' / '.join(x for x in rules if x.endswith('_cmd'))
|
|
337
431
|
cmds = f'init = ws* ( {cmds} ) ws* ";"? ws*\n'
|
|
338
432
|
|
|
339
|
-
return
|
|
433
|
+
return (
|
|
434
|
+
Grammar(cmds + CORE_GRAMMAR + '\n'.join(out)), command_key,
|
|
435
|
+
rule_info, syntax_txt, help_txt,
|
|
436
|
+
)
|
|
340
437
|
|
|
341
438
|
|
|
342
439
|
def flatten(items: Iterable[Any]) -> List[Any]:
|
|
@@ -378,7 +475,10 @@ class SQLHandler(NodeVisitor):
|
|
|
378
475
|
#: Metadata about the parse rules
|
|
379
476
|
rule_info: Dict[str, Any] = {}
|
|
380
477
|
|
|
381
|
-
#:
|
|
478
|
+
#: Syntax string for use in error messages
|
|
479
|
+
syntax: str = ''
|
|
480
|
+
|
|
481
|
+
#: Full help for the command
|
|
382
482
|
help: str = ''
|
|
383
483
|
|
|
384
484
|
#: Rule validation functions
|
|
@@ -396,7 +496,7 @@ class SQLHandler(NodeVisitor):
|
|
|
396
496
|
Compile the grammar held in the docstring.
|
|
397
497
|
|
|
398
498
|
This method modifies attributes on the class: ``grammar``,
|
|
399
|
-
``command_key``, ``rule_info``, and ``help``.
|
|
499
|
+
``command_key``, ``rule_info``, ``syntax``, and ``help``.
|
|
400
500
|
|
|
401
501
|
Parameters
|
|
402
502
|
----------
|
|
@@ -407,7 +507,7 @@ class SQLHandler(NodeVisitor):
|
|
|
407
507
|
if cls._is_compiled:
|
|
408
508
|
return
|
|
409
509
|
|
|
410
|
-
cls.grammar, cls.command_key, cls.rule_info, cls.help = \
|
|
510
|
+
cls.grammar, cls.command_key, cls.rule_info, cls.syntax, cls.help = \
|
|
411
511
|
process_grammar(grammar or cls.__doc__ or '')
|
|
412
512
|
|
|
413
513
|
cls._grammar = grammar or cls.__doc__ or ''
|
|
@@ -476,7 +576,7 @@ class SQLHandler(NodeVisitor):
|
|
|
476
576
|
msg = ' ' + m.group(1) + m.group(2)
|
|
477
577
|
raise ValueError(
|
|
478
578
|
f'Could not parse statement.{msg} '
|
|
479
|
-
'Expecting:\n' + textwrap.indent(type(self).
|
|
579
|
+
'Expecting:\n' + textwrap.indent(type(self).syntax, ' '),
|
|
480
580
|
)
|
|
481
581
|
|
|
482
582
|
@abc.abstractmethod
|
|
@@ -33,6 +33,41 @@ class ShowStageFilesHandler(SQLHandler):
|
|
|
33
33
|
# Should extended attributes be shown?
|
|
34
34
|
extended = EXTENDED
|
|
35
35
|
|
|
36
|
+
Description
|
|
37
|
+
-----------
|
|
38
|
+
Show the files in a workspace group's stage.
|
|
39
|
+
|
|
40
|
+
Remarks
|
|
41
|
+
-------
|
|
42
|
+
* ``IN GROUP`` specifies the workspace group or workspace group ID.
|
|
43
|
+
When using an ID, ``IN GROUP ID`` must be used.
|
|
44
|
+
* ``AT PATH`` specifies the path to list. If no ``AT PATH`` is specified,
|
|
45
|
+
the root directory is used.
|
|
46
|
+
* ``LIKE`` allows you to specify a filename pattern using ``%`` as a wildcard.
|
|
47
|
+
* ``ORDER BY`` allows you to specify the field to sort by.
|
|
48
|
+
* ``LIMIT`` allows you to set a limit on the number of entries displayed.
|
|
49
|
+
* ``RECURSIVE`` indicates that the stage should be listed recursively.
|
|
50
|
+
* ``EXTENDED`` indicates that more detailed information should be displayed.
|
|
51
|
+
|
|
52
|
+
Examples
|
|
53
|
+
--------
|
|
54
|
+
Example 1: Show files at path
|
|
55
|
+
|
|
56
|
+
This example shows how to list files starting at a specific path::
|
|
57
|
+
|
|
58
|
+
SHOW STAGE FILES IN GROUP "My Group" AT PATH "/data/";
|
|
59
|
+
|
|
60
|
+
Example 2: Show files recursively
|
|
61
|
+
|
|
62
|
+
This example show dow to display files recursively and with extra information::
|
|
63
|
+
|
|
64
|
+
SHOW STAGE FILES IN GROUP "My Group" RECURSIVE EXTENDED;
|
|
65
|
+
|
|
66
|
+
See Also
|
|
67
|
+
--------
|
|
68
|
+
* UPLOAD FILE TO STAGE
|
|
69
|
+
* DOWNLOAD STAGE FILE
|
|
70
|
+
|
|
36
71
|
"""
|
|
37
72
|
|
|
38
73
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
@@ -100,6 +135,30 @@ class UploadStageFileHandler(SQLHandler):
|
|
|
100
135
|
# Should an existing file be overwritten?
|
|
101
136
|
overwrite = OVERWRITE
|
|
102
137
|
|
|
138
|
+
Description
|
|
139
|
+
-----------
|
|
140
|
+
Upload a file to the workspace group's stage.
|
|
141
|
+
|
|
142
|
+
Remarks
|
|
143
|
+
-------
|
|
144
|
+
* ``<stage-path>`` is the path in stage to upload the file to.
|
|
145
|
+
* ``IN GROUP`` specifies the workspace group or workspace group ID. When
|
|
146
|
+
using an ID ``IN GROUP ID`` should be used.
|
|
147
|
+
* ``<local-path>`` is the path on the local machine of the file to upload.
|
|
148
|
+
* ``OVERWRITE`` indicates that an existing stage file at that path
|
|
149
|
+
should be overwritten if it exists.
|
|
150
|
+
|
|
151
|
+
Examples
|
|
152
|
+
--------
|
|
153
|
+
Example 1: Upload with overwrite::
|
|
154
|
+
|
|
155
|
+
UPLOAD FILE TO STAGE '/data/stats.csv' IN GROUP 'My Group'
|
|
156
|
+
FROM '/u/user/stats.csv' OVERWRITE;
|
|
157
|
+
|
|
158
|
+
See Also
|
|
159
|
+
--------
|
|
160
|
+
* DOWNLOAD STAGE FILE
|
|
161
|
+
|
|
103
162
|
"""
|
|
104
163
|
|
|
105
164
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
@@ -140,6 +199,38 @@ class DownloadStageFileHandler(SQLHandler):
|
|
|
140
199
|
# File encoding
|
|
141
200
|
encoding = ENCODING '<encoding>'
|
|
142
201
|
|
|
202
|
+
Description
|
|
203
|
+
-----------
|
|
204
|
+
Download a stage file.
|
|
205
|
+
|
|
206
|
+
Remarks
|
|
207
|
+
-------
|
|
208
|
+
* ``<stage-path>`` is the path in stage to download.
|
|
209
|
+
* ``IN GROUP`` specifies the workspace group or workspace group ID. When
|
|
210
|
+
using an ID ``IN GROUP ID`` should be used.
|
|
211
|
+
* ``<local-path>`` is the destination path for the file. If not specified,
|
|
212
|
+
the file is returned in a result set.
|
|
213
|
+
* ``OVERWRITE`` indicates that an existing local file should be overwritten.
|
|
214
|
+
* ``ENCODING`` specifies the encoding of the file to apply to the downloaded
|
|
215
|
+
file. By default, files are downloaded as binary. This only option
|
|
216
|
+
typically only matters if ``<local-path>`` is not specified and the file
|
|
217
|
+
is to be printed to the screen.
|
|
218
|
+
|
|
219
|
+
Examples
|
|
220
|
+
--------
|
|
221
|
+
Example 1: Print a file to the screen::
|
|
222
|
+
|
|
223
|
+
DOWNLOAD STAGE FILE '/data/stats.csv' IN GROUP 'My Group';
|
|
224
|
+
|
|
225
|
+
Example 2: Download a file to a local path with overwrite set::
|
|
226
|
+
|
|
227
|
+
DONLOAD STAGE FILE '/data/stats.csv' IN GROUP 'My Group'
|
|
228
|
+
TO '/u/me/data.csv' OVERWRITE;
|
|
229
|
+
|
|
230
|
+
See Also
|
|
231
|
+
--------
|
|
232
|
+
* UPLOAD FILE TO STAGE
|
|
233
|
+
|
|
143
234
|
"""
|
|
144
235
|
|
|
145
236
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
@@ -183,6 +274,26 @@ class DropStageFileHandler(SQLHandler):
|
|
|
183
274
|
# Name of group
|
|
184
275
|
group_name = '<group-name>'
|
|
185
276
|
|
|
277
|
+
Description
|
|
278
|
+
-----------
|
|
279
|
+
Drop a stage file.
|
|
280
|
+
|
|
281
|
+
Remarks
|
|
282
|
+
-------
|
|
283
|
+
* ``<stage-path>`` is the path in stage to drop.
|
|
284
|
+
* ``IN GROUP`` specifies the workspace group or workspace group ID. When
|
|
285
|
+
using an ID ``IN GROUP ID`` should be used.
|
|
286
|
+
|
|
287
|
+
Example
|
|
288
|
+
--------
|
|
289
|
+
Drop a specific file from stage::
|
|
290
|
+
|
|
291
|
+
DROP STAGE FILE '/data/stats.csv' IN GROUP 'My Group';
|
|
292
|
+
|
|
293
|
+
See Also
|
|
294
|
+
--------
|
|
295
|
+
* DROP STAGE FOLDER
|
|
296
|
+
|
|
186
297
|
"""
|
|
187
298
|
|
|
188
299
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
@@ -213,6 +324,27 @@ class DropStageFolderHandler(SQLHandler):
|
|
|
213
324
|
# Should folers be deleted recursively?
|
|
214
325
|
recursive = RECURSIVE
|
|
215
326
|
|
|
327
|
+
Description
|
|
328
|
+
-----------
|
|
329
|
+
Drop a folder from stage.
|
|
330
|
+
|
|
331
|
+
Remarks
|
|
332
|
+
-------
|
|
333
|
+
* ``<stage-path>`` is the path in stage to drop.
|
|
334
|
+
* ``IN GROUP`` specifies the workspace group or workspace group ID. When
|
|
335
|
+
using an ID ``IN GROUP ID`` should be used.
|
|
336
|
+
* ``RECURSIVE`` indicates that folders should be removed recursively.
|
|
337
|
+
|
|
338
|
+
Example
|
|
339
|
+
-------
|
|
340
|
+
Drop a folder recursively::
|
|
341
|
+
|
|
342
|
+
DROP STAGE FOLDER '/data/' IN GROUP 'My Group' RECURSIVE;
|
|
343
|
+
|
|
344
|
+
See Also
|
|
345
|
+
--------
|
|
346
|
+
* DROP STAGE FILE
|
|
347
|
+
|
|
216
348
|
"""
|
|
217
349
|
|
|
218
350
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
@@ -246,6 +378,24 @@ class CreateStageFolderHandler(SQLHandler):
|
|
|
246
378
|
# Should an existing folder be overwritten?
|
|
247
379
|
overwrite = OVERWRITE
|
|
248
380
|
|
|
381
|
+
Description
|
|
382
|
+
-----------
|
|
383
|
+
Create a folder in stage.
|
|
384
|
+
|
|
385
|
+
Remarks
|
|
386
|
+
-------
|
|
387
|
+
* ``<stage-path>`` is the path to create in stage.
|
|
388
|
+
* ``IN GROUP`` specifies the workspace group or workspace group ID. When
|
|
389
|
+
using an ID ``IN GROUP ID`` should be used.
|
|
390
|
+
* ``OVERWRITE`` indicates that an existing folder should be overwritten
|
|
391
|
+
with a new folder.
|
|
392
|
+
|
|
393
|
+
Example
|
|
394
|
+
-------
|
|
395
|
+
Create a folder::
|
|
396
|
+
|
|
397
|
+
CREATE STAGE FOLDER `/data/csv/` IN GROUP 'My Group';
|
|
398
|
+
|
|
249
399
|
"""
|
|
250
400
|
|
|
251
401
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|