singlestoredb 1.7.2__cp38-abi3-win_amd64.whl → 1.8.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/fusion/handler.py +68 -18
- singlestoredb/fusion/handlers/export.py +245 -0
- singlestoredb/fusion/handlers/job.py +4 -20
- singlestoredb/management/export.py +310 -0
- singlestoredb/py.typed +0 -0
- singlestoredb/tests/test_fusion.py +0 -4
- {singlestoredb-1.7.2.dist-info → singlestoredb-1.8.0.dist-info}/METADATA +1 -1
- {singlestoredb-1.7.2.dist-info → singlestoredb-1.8.0.dist-info}/RECORD +14 -11
- {singlestoredb-1.7.2.dist-info → singlestoredb-1.8.0.dist-info}/LICENSE +0 -0
- {singlestoredb-1.7.2.dist-info → singlestoredb-1.8.0.dist-info}/WHEEL +0 -0
- {singlestoredb-1.7.2.dist-info → singlestoredb-1.8.0.dist-info}/entry_points.txt +0 -0
- {singlestoredb-1.7.2.dist-info → singlestoredb-1.8.0.dist-info}/top_level.txt +0 -0
_singlestoredb_accel.pyd
CHANGED
|
Binary file
|
singlestoredb/__init__.py
CHANGED
singlestoredb/fusion/handler.py
CHANGED
|
@@ -11,6 +11,7 @@ from typing import Dict
|
|
|
11
11
|
from typing import Iterable
|
|
12
12
|
from typing import List
|
|
13
13
|
from typing import Optional
|
|
14
|
+
from typing import Set
|
|
14
15
|
from typing import Tuple
|
|
15
16
|
|
|
16
17
|
from parsimonious import Grammar
|
|
@@ -23,9 +24,9 @@ from ..connection import Connection
|
|
|
23
24
|
|
|
24
25
|
CORE_GRAMMAR = r'''
|
|
25
26
|
ws = ~r"(\s+|(\s*/\*.*\*/\s*)+)"
|
|
26
|
-
qs = ~r"\"([^\"]*)\"|'([^\']*)'
|
|
27
|
-
number = ~r"[-+]?(\d*\.)?\d+(e[-+]?\d+)?"i
|
|
28
|
-
integer = ~r"-?\d+"
|
|
27
|
+
qs = ~r"\"([^\"]*)\"|'([^\']*)'|([A-Za-z0-9_\-\.]+)|`([^\`]+)`" ws*
|
|
28
|
+
number = ~r"[-+]?(\d*\.)?\d+(e[-+]?\d+)?"i ws*
|
|
29
|
+
integer = ~r"-?\d+" ws*
|
|
29
30
|
comma = ws* "," ws*
|
|
30
31
|
eq = ws* "=" ws*
|
|
31
32
|
open_paren = ws* "(" ws*
|
|
@@ -33,6 +34,10 @@ CORE_GRAMMAR = r'''
|
|
|
33
34
|
open_repeats = ws* ~r"[\(\[\{]" ws*
|
|
34
35
|
close_repeats = ws* ~r"[\)\]\}]" ws*
|
|
35
36
|
select = ~r"SELECT"i ws+ ~r".+" ws*
|
|
37
|
+
table = ~r"(?:([A-Za-z0-9_\-]+)|`([^\`]+)`)(?:\.(?:([A-Za-z0-9_\-]+)|`([^\`]+)`))?" ws*
|
|
38
|
+
column = ~r"(?:([A-Za-z0-9_\-]+)|`([^\`]+)`)(?:\.(?:([A-Za-z0-9_\-]+)|`([^\`]+)`))?" ws*
|
|
39
|
+
link_name = ~r"(?:([A-Za-z0-9_\-]+)|`([^\`]+)`)(?:\.(?:([A-Za-z0-9_\-]+)|`([^\`]+)`))?" ws*
|
|
40
|
+
catalog_name = ~r"(?:([A-Za-z0-9_\-]+)|`([^\`]+)`)(?:\.(?:([A-Za-z0-9_\-]+)|`([^\`]+)`))?" ws*
|
|
36
41
|
|
|
37
42
|
json = ws* json_object ws*
|
|
38
43
|
json_object = ~r"{\s*" json_members? ~r"\s*}"
|
|
@@ -65,6 +70,10 @@ BUILTINS = {
|
|
|
65
70
|
'<integer>': '',
|
|
66
71
|
'<number>': '',
|
|
67
72
|
'<json>': '',
|
|
73
|
+
'<table>': '',
|
|
74
|
+
'<column>': '',
|
|
75
|
+
'<catalog-name>': '',
|
|
76
|
+
'<link-name>': '',
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
BUILTIN_DEFAULTS = { # type: ignore
|
|
@@ -226,9 +235,13 @@ def build_syntax(grammar: str) -> str:
|
|
|
226
235
|
# Split on ';' on a line by itself
|
|
227
236
|
cmd, end = grammar.split(';', 1)
|
|
228
237
|
|
|
229
|
-
|
|
238
|
+
name = ''
|
|
239
|
+
rules: Dict[str, Any] = {}
|
|
230
240
|
for line in end.split('\n'):
|
|
231
241
|
line = line.strip()
|
|
242
|
+
if line.startswith('&'):
|
|
243
|
+
rules[name] += '\n' + line
|
|
244
|
+
continue
|
|
232
245
|
if not line:
|
|
233
246
|
continue
|
|
234
247
|
name, value = line.split('=', 1)
|
|
@@ -239,10 +252,16 @@ def build_syntax(grammar: str) -> str:
|
|
|
239
252
|
while re.search(r' [a-z0-9_]+\b', cmd):
|
|
240
253
|
cmd = re.sub(r' ([a-z0-9_]+)\b', functools.partial(expand_rules, rules), cmd)
|
|
241
254
|
|
|
255
|
+
def add_indent(m: Any) -> str:
|
|
256
|
+
return ' ' + (len(m.group(1)) * ' ')
|
|
257
|
+
|
|
258
|
+
# Indent line-continuations
|
|
259
|
+
cmd = re.sub(r'^(\&+)\s*', add_indent, cmd, flags=re.M)
|
|
260
|
+
|
|
242
261
|
cmd = textwrap.dedent(cmd).rstrip() + ';'
|
|
243
|
-
cmd = re.sub(r' +', ' ', cmd)
|
|
244
|
-
cmd = re.sub(r'
|
|
245
|
-
cmd = re.sub(r'\s
|
|
262
|
+
cmd = re.sub(r'(\S) +', r'\1 ', cmd)
|
|
263
|
+
cmd = re.sub(r'<comma>', ',', cmd)
|
|
264
|
+
cmd = re.sub(r'\s+,\s*\.\.\.', ',...', cmd)
|
|
246
265
|
|
|
247
266
|
return cmd
|
|
248
267
|
|
|
@@ -399,9 +418,15 @@ def process_grammar(
|
|
|
399
418
|
help_txt = build_help(syntax_txt, full_grammar)
|
|
400
419
|
grammar = build_cmd(grammar)
|
|
401
420
|
|
|
421
|
+
# Remove line-continuations
|
|
422
|
+
grammar = re.sub(r'\n\s*&+', r'', grammar)
|
|
423
|
+
|
|
402
424
|
# Make sure grouping characters all have whitespace around them
|
|
403
425
|
grammar = re.sub(r' *(\[|\{|\||\}|\]) *', r' \1 ', grammar)
|
|
404
426
|
|
|
427
|
+
grammar = re.sub(r'\(', r' open_paren ', grammar)
|
|
428
|
+
grammar = re.sub(r'\)', r' close_paren ', grammar)
|
|
429
|
+
|
|
405
430
|
for line in grammar.split('\n'):
|
|
406
431
|
if not line.strip():
|
|
407
432
|
continue
|
|
@@ -418,7 +443,7 @@ def process_grammar(
|
|
|
418
443
|
sql = re.sub(r'\]\s+\[', r' | ', sql)
|
|
419
444
|
|
|
420
445
|
# Lower-case keywords and make them case-insensitive
|
|
421
|
-
sql = re.sub(r'(\b|@+)([A-Z0-
|
|
446
|
+
sql = re.sub(r'(\b|@+)([A-Z0-9_]+)\b', lower_and_regex, sql)
|
|
422
447
|
|
|
423
448
|
# Convert literal strings to 'qs'
|
|
424
449
|
sql = re.sub(r"'[^']+'", r'qs', sql)
|
|
@@ -461,12 +486,18 @@ def process_grammar(
|
|
|
461
486
|
sql = re.sub(r'\s+ws$', r' ws*', sql)
|
|
462
487
|
sql = re.sub(r'\s+ws\s+\(', r' ws* (', sql)
|
|
463
488
|
sql = re.sub(r'\)\s+ws\s+', r') ws* ', sql)
|
|
464
|
-
sql = re.sub(r'\s+ws\s+', r' ws
|
|
489
|
+
sql = re.sub(r'\s+ws\s+', r' ws* ', sql)
|
|
465
490
|
sql = re.sub(r'\?\s+ws\+', r'? ws*', sql)
|
|
466
491
|
|
|
467
492
|
# Remove extra ws around eq
|
|
468
493
|
sql = re.sub(r'ws\+\s*eq\b', r'eq', sql)
|
|
469
494
|
|
|
495
|
+
# Remove optional groupings when mandatory groupings are specified
|
|
496
|
+
sql = re.sub(r'open_paren\s+ws\*\s+open_repeats\?', r'open_paren', sql)
|
|
497
|
+
sql = re.sub(r'close_repeats\?\s+ws\*\s+close_paren', r'close_paren', sql)
|
|
498
|
+
sql = re.sub(r'open_paren\s+open_repeats\?', r'open_paren', sql)
|
|
499
|
+
sql = re.sub(r'close_repeats\?\s+close_paren', r'close_paren', sql)
|
|
500
|
+
|
|
470
501
|
out.append(f'{op} = {sql}')
|
|
471
502
|
|
|
472
503
|
for k, v in list(rules.items()):
|
|
@@ -548,6 +579,7 @@ class SQLHandler(NodeVisitor):
|
|
|
548
579
|
|
|
549
580
|
def __init__(self, connection: Connection):
|
|
550
581
|
self.connection = connection
|
|
582
|
+
self._handled: Set[str] = set()
|
|
551
583
|
|
|
552
584
|
@classmethod
|
|
553
585
|
def compile(cls, grammar: str = '') -> None:
|
|
@@ -614,12 +646,16 @@ class SQLHandler(NodeVisitor):
|
|
|
614
646
|
)
|
|
615
647
|
|
|
616
648
|
type(self).compile()
|
|
649
|
+
self._handled = set()
|
|
617
650
|
try:
|
|
618
651
|
params = self.visit(type(self).grammar.parse(sql))
|
|
619
652
|
for k, v in params.items():
|
|
620
653
|
params[k] = self.validate_rule(k, v)
|
|
621
654
|
|
|
622
655
|
res = self.run(params)
|
|
656
|
+
|
|
657
|
+
self._handled = set()
|
|
658
|
+
|
|
623
659
|
if res is not None:
|
|
624
660
|
res.format_results(self.connection)
|
|
625
661
|
return res
|
|
@@ -666,16 +702,20 @@ class SQLHandler(NodeVisitor):
|
|
|
666
702
|
"""Quoted strings."""
|
|
667
703
|
if node is None:
|
|
668
704
|
return None
|
|
669
|
-
return
|
|
670
|
-
|
|
705
|
+
return flatten(visited_children)[0]
|
|
706
|
+
|
|
707
|
+
def visit_compound(self, node: Node, visited_children: Iterable[Any]) -> Any:
|
|
708
|
+
"""Compound name."""
|
|
709
|
+
print(visited_children)
|
|
710
|
+
return flatten(visited_children)[0]
|
|
671
711
|
|
|
672
712
|
def visit_number(self, node: Node, visited_children: Iterable[Any]) -> Any:
|
|
673
713
|
"""Numeric value."""
|
|
674
|
-
return float(
|
|
714
|
+
return float(flatten(visited_children)[0])
|
|
675
715
|
|
|
676
716
|
def visit_integer(self, node: Node, visited_children: Iterable[Any]) -> Any:
|
|
677
717
|
"""Integer value."""
|
|
678
|
-
return int(
|
|
718
|
+
return int(flatten(visited_children)[0])
|
|
679
719
|
|
|
680
720
|
def visit_ws(self, node: Node, visited_children: Iterable[Any]) -> Any:
|
|
681
721
|
"""Whitespace and comments."""
|
|
@@ -804,19 +844,29 @@ class SQLHandler(NodeVisitor):
|
|
|
804
844
|
if node.expr_name.endswith('_cmd'):
|
|
805
845
|
final = merge_dicts(flatten(visited_children)[n_keywords:])
|
|
806
846
|
for k, v in type(self).rule_info.items():
|
|
807
|
-
if k.endswith('_cmd') or k.endswith('_'):
|
|
847
|
+
if k.endswith('_cmd') or k.endswith('_') or k.startswith('_'):
|
|
808
848
|
continue
|
|
809
|
-
if k not in final:
|
|
849
|
+
if k not in final and k not in self._handled:
|
|
810
850
|
final[k] = BUILTIN_DEFAULTS.get(k, v['default'])
|
|
811
851
|
return final
|
|
812
852
|
|
|
813
853
|
# Filter out stray empty strings
|
|
814
854
|
out = [x for x in flatten(visited_children)[n_keywords:] if x]
|
|
815
855
|
|
|
816
|
-
|
|
817
|
-
|
|
856
|
+
# Remove underscore prefixes from rule name
|
|
857
|
+
key_name = re.sub(r'^_+', r'', node.expr_name)
|
|
818
858
|
|
|
819
|
-
|
|
859
|
+
if repeats or len(out) > 1:
|
|
860
|
+
self._handled.add(node.expr_name)
|
|
861
|
+
# If all outputs are dicts, merge them
|
|
862
|
+
if len(out) > 1 and not repeats:
|
|
863
|
+
is_dicts = [x for x in out if isinstance(x, dict)]
|
|
864
|
+
if len(is_dicts) == len(out):
|
|
865
|
+
return {key_name: merge_dicts(out)}
|
|
866
|
+
return {key_name: out}
|
|
867
|
+
|
|
868
|
+
self._handled.add(node.expr_name)
|
|
869
|
+
return {key_name: out[0] if out else True}
|
|
820
870
|
|
|
821
871
|
if hasattr(node, 'match'):
|
|
822
872
|
if not visited_children and not node.match.groups():
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import json
|
|
3
|
+
from typing import Any
|
|
4
|
+
from typing import Dict
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from .. import result
|
|
8
|
+
from ...management.export import Catalog
|
|
9
|
+
from ...management.export import ExportService
|
|
10
|
+
from ...management.export import ExportStatus
|
|
11
|
+
from ...management.export import Link
|
|
12
|
+
from ..handler import SQLHandler
|
|
13
|
+
from ..result import FusionSQLResult
|
|
14
|
+
from .utils import get_workspace_group
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CreateClusterIdentity(SQLHandler):
|
|
18
|
+
"""
|
|
19
|
+
CREATE CLUSTER IDENTITY
|
|
20
|
+
catalog
|
|
21
|
+
storage
|
|
22
|
+
;
|
|
23
|
+
|
|
24
|
+
# Catolog
|
|
25
|
+
catalog = CATALOG { _catalog_config | _catalog_creds }
|
|
26
|
+
_catalog_config = CONFIG '<catalog-config>'
|
|
27
|
+
_catalog_creds = CREDENTIALS '<catalog-creds>'
|
|
28
|
+
|
|
29
|
+
# Storage
|
|
30
|
+
storage = LINK { _link_config | _link_creds }
|
|
31
|
+
_link_config = S3 CONFIG '<link-config>'
|
|
32
|
+
_link_creds = CREDENTIALS '<link-creds>'
|
|
33
|
+
|
|
34
|
+
Description
|
|
35
|
+
-----------
|
|
36
|
+
Create a cluster identity for allowing the export service to access
|
|
37
|
+
external cloud resources.
|
|
38
|
+
|
|
39
|
+
Arguments
|
|
40
|
+
---------
|
|
41
|
+
* ``<catalog-config>`` and ``<catalog-creds>``: Catalog configuration
|
|
42
|
+
and credentials in JSON format.
|
|
43
|
+
* ``<link-config>`` and ``<link-creds>``: Storage link configuration
|
|
44
|
+
and credentials in JSON format.
|
|
45
|
+
|
|
46
|
+
Remarks
|
|
47
|
+
-------
|
|
48
|
+
* ``FROM <table>`` specifies the SingleStore table to export. The same name will
|
|
49
|
+
be used for the exported table.
|
|
50
|
+
* ``CATALOG`` specifies the details of the catalog to connect to.
|
|
51
|
+
* ``LINK`` specifies the details of the data storage to connect to.
|
|
52
|
+
|
|
53
|
+
Example
|
|
54
|
+
-------
|
|
55
|
+
The following statement creates a cluster identity for the catalog
|
|
56
|
+
and link::
|
|
57
|
+
|
|
58
|
+
CREATE CLUSTER IDENTITY
|
|
59
|
+
CATALOG CONFIG '{
|
|
60
|
+
"type": "GLUE",
|
|
61
|
+
"table_format": "ICEBERG",
|
|
62
|
+
"id": "13983498723498",
|
|
63
|
+
"region": "us-east-1"
|
|
64
|
+
}'
|
|
65
|
+
LINK S3 CONFIG '{
|
|
66
|
+
"region": "us-east-1",
|
|
67
|
+
"endpoint_url": "s3://bucket-name"
|
|
68
|
+
|
|
69
|
+
}'
|
|
70
|
+
;
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
_enabled = False
|
|
75
|
+
|
|
76
|
+
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
77
|
+
# Catalog
|
|
78
|
+
catalog_config = json.loads(params['catalog'].get('catalog_config', '{}') or '{}')
|
|
79
|
+
catalog_creds = json.loads(params['catalog'].get('catalog_creds', '{}') or '{}')
|
|
80
|
+
|
|
81
|
+
# Storage
|
|
82
|
+
storage_config = json.loads(params['storage'].get('link_config', '{}') or '{}')
|
|
83
|
+
storage_creds = json.loads(params['storage'].get('link_creds', '{}') or '{}')
|
|
84
|
+
|
|
85
|
+
wsg = get_workspace_group({})
|
|
86
|
+
|
|
87
|
+
if wsg._manager is None:
|
|
88
|
+
raise TypeError('no workspace manager is associated with workspace group')
|
|
89
|
+
|
|
90
|
+
out = ExportService(
|
|
91
|
+
wsg,
|
|
92
|
+
'none',
|
|
93
|
+
'none',
|
|
94
|
+
Catalog.from_config_and_creds(catalog_config, catalog_creds, wsg._manager),
|
|
95
|
+
Link.from_config_and_creds('S3', storage_config, storage_creds, wsg._manager),
|
|
96
|
+
columns=None,
|
|
97
|
+
).create_cluster_identity()
|
|
98
|
+
|
|
99
|
+
res = FusionSQLResult()
|
|
100
|
+
res.add_field('RoleARN', result.STRING)
|
|
101
|
+
res.set_rows([(out['roleARN'],)])
|
|
102
|
+
|
|
103
|
+
return res
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
CreateClusterIdentity.register(overwrite=True)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class CreateExport(SQLHandler):
|
|
110
|
+
"""
|
|
111
|
+
START EXPORT
|
|
112
|
+
from_table
|
|
113
|
+
catalog
|
|
114
|
+
storage
|
|
115
|
+
;
|
|
116
|
+
|
|
117
|
+
# From table
|
|
118
|
+
from_table = FROM <table>
|
|
119
|
+
|
|
120
|
+
# Catolog
|
|
121
|
+
catalog = CATALOG [ _catalog_config ] [ _catalog_creds ]
|
|
122
|
+
_catalog_config = CONFIG '<catalog-config>'
|
|
123
|
+
_catalog_creds = CREDENTIALS '<catalog-creds>'
|
|
124
|
+
|
|
125
|
+
# Storage
|
|
126
|
+
storage = LINK [ _link_config ] [ _link_creds ]
|
|
127
|
+
_link_config = S3 CONFIG '<link-config>'
|
|
128
|
+
_link_creds = CREDENTIALS '<link-creds>'
|
|
129
|
+
|
|
130
|
+
Description
|
|
131
|
+
-----------
|
|
132
|
+
Create an export configuration.
|
|
133
|
+
|
|
134
|
+
Arguments
|
|
135
|
+
---------
|
|
136
|
+
* ``<catalog-config>`` and ``<catalog-creds>``: The catalog configuration.
|
|
137
|
+
* ``<link-config>`` and ``<link-creds>``: The storage link configuration.
|
|
138
|
+
|
|
139
|
+
Remarks
|
|
140
|
+
-------
|
|
141
|
+
* ``FROM <table>`` specifies the SingleStore table to export. The same name will
|
|
142
|
+
be used for the exported table.
|
|
143
|
+
* ``CATALOG`` specifies the details of the catalog to connect to.
|
|
144
|
+
* ``LINK`` specifies the details of the data storage to connect to.
|
|
145
|
+
|
|
146
|
+
Examples
|
|
147
|
+
--------
|
|
148
|
+
The following statement starts an export operation with the given
|
|
149
|
+
catalog and link configurations. The source table to export is
|
|
150
|
+
named "customer_data"::
|
|
151
|
+
|
|
152
|
+
START EXPORT FROM customer_data
|
|
153
|
+
CATALOG CONFIG '{
|
|
154
|
+
"type": "GLUE",
|
|
155
|
+
"table_format": "ICEBERG",
|
|
156
|
+
"id": "13983498723498",
|
|
157
|
+
"region": "us-east-1"
|
|
158
|
+
}'
|
|
159
|
+
LINK S3 CONFIG '{
|
|
160
|
+
"region": "us-east-1",
|
|
161
|
+
"endpoint_url": "s3://bucket-name"
|
|
162
|
+
|
|
163
|
+
}'
|
|
164
|
+
;
|
|
165
|
+
|
|
166
|
+
""" # noqa
|
|
167
|
+
|
|
168
|
+
_enabled = False
|
|
169
|
+
|
|
170
|
+
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
171
|
+
# From table
|
|
172
|
+
if isinstance(params['from_table'], str):
|
|
173
|
+
from_database = None
|
|
174
|
+
from_table = params['from_table']
|
|
175
|
+
else:
|
|
176
|
+
from_database, from_table = params['from_table']
|
|
177
|
+
|
|
178
|
+
# Catalog
|
|
179
|
+
catalog_config = json.loads(params['catalog'].get('catalog_config', '{}') or '{}')
|
|
180
|
+
catalog_creds = json.loads(params['catalog'].get('catalog_creds', '{}') or '{}')
|
|
181
|
+
|
|
182
|
+
# Storage
|
|
183
|
+
storage_config = json.loads(params['storage'].get('link_config', '{}') or '{}')
|
|
184
|
+
storage_creds = json.loads(params['storage'].get('link_creds', '{}') or '{}')
|
|
185
|
+
|
|
186
|
+
wsg = get_workspace_group({})
|
|
187
|
+
|
|
188
|
+
if from_database is None:
|
|
189
|
+
raise ValueError('database name must be specified for source table')
|
|
190
|
+
|
|
191
|
+
if wsg._manager is None:
|
|
192
|
+
raise TypeError('no workspace manager is associated with workspace group')
|
|
193
|
+
|
|
194
|
+
out = ExportService(
|
|
195
|
+
wsg,
|
|
196
|
+
from_database,
|
|
197
|
+
from_table,
|
|
198
|
+
Catalog.from_config_and_creds(catalog_config, catalog_creds, wsg._manager),
|
|
199
|
+
Link.from_config_and_creds('S3', storage_config, storage_creds, wsg._manager),
|
|
200
|
+
columns=None,
|
|
201
|
+
).start()
|
|
202
|
+
|
|
203
|
+
res = FusionSQLResult()
|
|
204
|
+
res.add_field('ExportID', result.STRING)
|
|
205
|
+
res.set_rows([(out.export_id,)])
|
|
206
|
+
|
|
207
|
+
return res
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
CreateExport.register(overwrite=True)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class ShowExport(SQLHandler):
|
|
214
|
+
"""
|
|
215
|
+
SHOW EXPORT export_id;
|
|
216
|
+
|
|
217
|
+
# ID of export
|
|
218
|
+
export_id = '<export-id>'
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
_enabled = False
|
|
223
|
+
|
|
224
|
+
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
225
|
+
wsg = get_workspace_group({})
|
|
226
|
+
out = ExportStatus(params['export_id'], wsg)
|
|
227
|
+
|
|
228
|
+
status = out._info()
|
|
229
|
+
|
|
230
|
+
res = FusionSQLResult()
|
|
231
|
+
res.add_field('ExportID', result.STRING)
|
|
232
|
+
res.add_field('Status', result.STRING)
|
|
233
|
+
res.add_field('Message', result.STRING)
|
|
234
|
+
res.set_rows([
|
|
235
|
+
(
|
|
236
|
+
params['export_id'],
|
|
237
|
+
status.get('status', 'Unknown'),
|
|
238
|
+
status.get('statusMsg', ''),
|
|
239
|
+
),
|
|
240
|
+
])
|
|
241
|
+
|
|
242
|
+
return res
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
ShowExport.register(overwrite=True)
|
|
@@ -122,8 +122,6 @@ class ScheduleJobHandler(SQLHandler):
|
|
|
122
122
|
;
|
|
123
123
|
"""
|
|
124
124
|
|
|
125
|
-
_enabled = False
|
|
126
|
-
|
|
127
125
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
128
126
|
res = FusionSQLResult()
|
|
129
127
|
res.add_field('JobID', result.STRING)
|
|
@@ -138,8 +136,8 @@ class ScheduleJobHandler(SQLHandler):
|
|
|
138
136
|
|
|
139
137
|
execution_interval_in_mins = None
|
|
140
138
|
if params.get('execute_every'):
|
|
141
|
-
execution_interval_in_mins = params['execute_every'][
|
|
142
|
-
time_unit = params['execute_every'][
|
|
139
|
+
execution_interval_in_mins = params['execute_every']['interval']
|
|
140
|
+
time_unit = params['execute_every']['time_unit'].upper()
|
|
143
141
|
if time_unit == 'MINUTES':
|
|
144
142
|
pass
|
|
145
143
|
elif time_unit == 'HOURS':
|
|
@@ -224,8 +222,6 @@ class RunJobHandler(SQLHandler):
|
|
|
224
222
|
|
|
225
223
|
"""
|
|
226
224
|
|
|
227
|
-
_enabled = False
|
|
228
|
-
|
|
229
225
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
230
226
|
res = FusionSQLResult()
|
|
231
227
|
res.add_field('JobID', result.STRING)
|
|
@@ -288,8 +284,6 @@ class WaitOnJobsHandler(SQLHandler):
|
|
|
288
284
|
|
|
289
285
|
"""
|
|
290
286
|
|
|
291
|
-
_enabled = False
|
|
292
|
-
|
|
293
287
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
294
288
|
res = FusionSQLResult()
|
|
295
289
|
res.add_field('Success', result.BOOL)
|
|
@@ -298,8 +292,8 @@ class WaitOnJobsHandler(SQLHandler):
|
|
|
298
292
|
|
|
299
293
|
timeout_in_secs = None
|
|
300
294
|
if params.get('with_timeout'):
|
|
301
|
-
timeout_in_secs = params['with_timeout'][
|
|
302
|
-
time_unit = params['with_timeout'][
|
|
295
|
+
timeout_in_secs = params['with_timeout']['time']
|
|
296
|
+
time_unit = params['with_timeout']['time_unit'].upper()
|
|
303
297
|
if time_unit == 'SECONDS':
|
|
304
298
|
pass
|
|
305
299
|
elif time_unit == 'MINUTES':
|
|
@@ -359,8 +353,6 @@ class ShowJobsHandler(SQLHandler):
|
|
|
359
353
|
|
|
360
354
|
"""
|
|
361
355
|
|
|
362
|
-
_enabled = False
|
|
363
|
-
|
|
364
356
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
365
357
|
res = FusionSQLResult()
|
|
366
358
|
res.add_field('JobID', result.STRING)
|
|
@@ -492,8 +484,6 @@ class ShowJobExecutionsHandler(SQLHandler):
|
|
|
492
484
|
EXTENDED;
|
|
493
485
|
"""
|
|
494
486
|
|
|
495
|
-
_enabled = False
|
|
496
|
-
|
|
497
487
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
498
488
|
res = FusionSQLResult()
|
|
499
489
|
res.add_field('ExecutionID', result.STRING)
|
|
@@ -564,8 +554,6 @@ class ShowJobParametersHandler(SQLHandler):
|
|
|
564
554
|
SHOW JOB PARAMETERS FOR 'job1';
|
|
565
555
|
"""
|
|
566
556
|
|
|
567
|
-
_enabled = False
|
|
568
|
-
|
|
569
557
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
570
558
|
res = FusionSQLResult()
|
|
571
559
|
res.add_field('Name', result.STRING)
|
|
@@ -606,8 +594,6 @@ class ShowJobRuntimesHandler(SQLHandler):
|
|
|
606
594
|
SHOW JOB RUNTIMES;
|
|
607
595
|
"""
|
|
608
596
|
|
|
609
|
-
_enabled = False
|
|
610
|
-
|
|
611
597
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
612
598
|
res = FusionSQLResult()
|
|
613
599
|
res.add_field('Name', result.STRING)
|
|
@@ -653,8 +639,6 @@ class DropJobHandler(SQLHandler):
|
|
|
653
639
|
DROP JOBS 'job1', 'job2';
|
|
654
640
|
"""
|
|
655
641
|
|
|
656
|
-
_enabled = False
|
|
657
|
-
|
|
658
642
|
def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:
|
|
659
643
|
res = FusionSQLResult()
|
|
660
644
|
res.add_field('JobID', result.STRING)
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""SingleStoreDB export service."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import abc
|
|
6
|
+
import re
|
|
7
|
+
from typing import Any
|
|
8
|
+
from typing import Dict
|
|
9
|
+
from typing import List
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from .. import ManagementError
|
|
13
|
+
from .utils import vars_to_str
|
|
14
|
+
from .workspace import WorkspaceGroup
|
|
15
|
+
from .workspace import WorkspaceManager
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Link(object):
|
|
19
|
+
"""Generic storage base class."""
|
|
20
|
+
scheme: str = 'unknown'
|
|
21
|
+
|
|
22
|
+
def __str__(self) -> str:
|
|
23
|
+
"""Return string representation."""
|
|
24
|
+
return vars_to_str(self)
|
|
25
|
+
|
|
26
|
+
def __repr__(self) -> str:
|
|
27
|
+
"""Return string representation."""
|
|
28
|
+
return str(self)
|
|
29
|
+
|
|
30
|
+
@abc.abstractmethod
|
|
31
|
+
def to_storage_location(self) -> Dict[str, Any]:
|
|
32
|
+
raise NotImplementedError
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def from_config_and_creds(
|
|
36
|
+
cls,
|
|
37
|
+
scheme: str,
|
|
38
|
+
config: Dict[str, Any],
|
|
39
|
+
credentials: Dict[str, Any],
|
|
40
|
+
manager: 'WorkspaceManager',
|
|
41
|
+
) -> 'Link':
|
|
42
|
+
out_cls = None
|
|
43
|
+
for c in cls.__subclasses__():
|
|
44
|
+
if c.scheme == scheme.upper():
|
|
45
|
+
out_cls = c
|
|
46
|
+
break
|
|
47
|
+
|
|
48
|
+
if out_cls is None:
|
|
49
|
+
raise TypeError(f'No link class found for given information: {scheme}')
|
|
50
|
+
|
|
51
|
+
return out_cls.from_config_and_creds(scheme, config, credentials, manager)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class S3Link(Link):
|
|
55
|
+
"""S3 link."""
|
|
56
|
+
|
|
57
|
+
scheme: str = 'S3'
|
|
58
|
+
region: str
|
|
59
|
+
storage_base_url: str
|
|
60
|
+
|
|
61
|
+
def __init__(self, region: str, storage_base_url: str):
|
|
62
|
+
self.region = region
|
|
63
|
+
self.storage_base_url = storage_base_url
|
|
64
|
+
self._manager: Optional[WorkspaceManager] = None
|
|
65
|
+
|
|
66
|
+
def to_storage_location(self) -> Dict[str, Any]:
|
|
67
|
+
return dict(
|
|
68
|
+
storageBaseURL=self.storage_base_url,
|
|
69
|
+
storageRegion=self.region,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def from_config_and_creds(
|
|
74
|
+
cls,
|
|
75
|
+
scheme: str,
|
|
76
|
+
config: Dict[str, Any],
|
|
77
|
+
credentials: Dict[str, Any],
|
|
78
|
+
manager: 'WorkspaceManager',
|
|
79
|
+
) -> 'S3Link':
|
|
80
|
+
assert scheme.upper() == cls.scheme
|
|
81
|
+
|
|
82
|
+
params: Dict[str, Any] = {}
|
|
83
|
+
params.update(config)
|
|
84
|
+
params.update(credentials)
|
|
85
|
+
|
|
86
|
+
assert params.get('region'), 'region is required'
|
|
87
|
+
assert params.get('endpoint_url'), 'endpoint_url is required'
|
|
88
|
+
|
|
89
|
+
out = cls(params['region'], params['endpoint_url'])
|
|
90
|
+
out._manager = manager
|
|
91
|
+
return out
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class Catalog(object):
|
|
95
|
+
"""Generic catalog base class."""
|
|
96
|
+
|
|
97
|
+
catalog_type: str = 'UNKNOWN'
|
|
98
|
+
table_format: str = 'UNKNOWN'
|
|
99
|
+
|
|
100
|
+
def __str__(self) -> str:
|
|
101
|
+
"""Return string representation."""
|
|
102
|
+
return vars_to_str(self)
|
|
103
|
+
|
|
104
|
+
def __repr__(self) -> str:
|
|
105
|
+
"""Return string representation."""
|
|
106
|
+
return str(self)
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def from_config_and_creds(
|
|
110
|
+
cls,
|
|
111
|
+
config: Dict[str, Any],
|
|
112
|
+
credentials: Dict[str, Any],
|
|
113
|
+
manager: 'WorkspaceManager',
|
|
114
|
+
) -> 'Catalog':
|
|
115
|
+
catalog_type = config['type'].upper()
|
|
116
|
+
table_format = config['table_format'].upper()
|
|
117
|
+
|
|
118
|
+
out_cls = None
|
|
119
|
+
for c in cls.__subclasses__():
|
|
120
|
+
if c.catalog_type == catalog_type and c.table_format == table_format:
|
|
121
|
+
out_cls = c
|
|
122
|
+
break
|
|
123
|
+
|
|
124
|
+
if out_cls is None:
|
|
125
|
+
raise TypeError(f'No catalog class found for given information: {config}')
|
|
126
|
+
|
|
127
|
+
return out_cls.from_config_and_creds(config, credentials, manager)
|
|
128
|
+
|
|
129
|
+
@abc.abstractmethod
|
|
130
|
+
def to_catalog_info(self) -> Dict[str, Any]:
|
|
131
|
+
"""Return a catalog info dictionary."""
|
|
132
|
+
raise NotImplementedError
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class IcebergGlueCatalog(Catalog):
|
|
136
|
+
"""Iceberg glue catalog."""
|
|
137
|
+
|
|
138
|
+
table_format = 'ICEBERG'
|
|
139
|
+
catalog_type = 'GLUE'
|
|
140
|
+
|
|
141
|
+
region: str
|
|
142
|
+
catalog_id: str
|
|
143
|
+
|
|
144
|
+
def __init__(self, region: str, catalog_id: str):
|
|
145
|
+
self.region = region
|
|
146
|
+
self.catalog_id = catalog_id
|
|
147
|
+
self._manager: Optional[WorkspaceManager] = None
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def from_config_and_creds(
|
|
151
|
+
cls,
|
|
152
|
+
config: Dict[str, Any],
|
|
153
|
+
credentials: Dict[str, Any],
|
|
154
|
+
manager: 'WorkspaceManager',
|
|
155
|
+
) -> 'IcebergGlueCatalog':
|
|
156
|
+
params = {}
|
|
157
|
+
params.update(config)
|
|
158
|
+
params.update(credentials)
|
|
159
|
+
|
|
160
|
+
out = cls(
|
|
161
|
+
region=params['region'],
|
|
162
|
+
catalog_id=params['id'],
|
|
163
|
+
)
|
|
164
|
+
out._manager = manager
|
|
165
|
+
return out
|
|
166
|
+
|
|
167
|
+
def to_catalog_info(self) -> Dict[str, Any]:
|
|
168
|
+
"""Return a catalog info dictionary."""
|
|
169
|
+
return dict(
|
|
170
|
+
catalogSource=self.catalog_type,
|
|
171
|
+
tableFormat=self.table_format,
|
|
172
|
+
glueRegion=self.region,
|
|
173
|
+
glueCatalogID=self.catalog_id,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class ExportService(object):
|
|
178
|
+
"""Export service."""
|
|
179
|
+
|
|
180
|
+
database: str
|
|
181
|
+
table: str
|
|
182
|
+
catalog: Catalog
|
|
183
|
+
storage_link: Link
|
|
184
|
+
columns: Optional[List[str]]
|
|
185
|
+
|
|
186
|
+
def __init__(
|
|
187
|
+
self,
|
|
188
|
+
workspace_group: WorkspaceGroup,
|
|
189
|
+
database: str,
|
|
190
|
+
table: str,
|
|
191
|
+
catalog: Catalog,
|
|
192
|
+
storage_link: Link,
|
|
193
|
+
columns: Optional[List[str]],
|
|
194
|
+
):
|
|
195
|
+
#: Workspace group
|
|
196
|
+
self.workspace_group = workspace_group
|
|
197
|
+
|
|
198
|
+
#: Name of SingleStoreDB database
|
|
199
|
+
self.database = database
|
|
200
|
+
|
|
201
|
+
#: Name of SingleStoreDB table
|
|
202
|
+
self.table = table
|
|
203
|
+
|
|
204
|
+
#: List of columns to export
|
|
205
|
+
self.columns = columns
|
|
206
|
+
|
|
207
|
+
#: Catalog
|
|
208
|
+
self.catalog = catalog
|
|
209
|
+
|
|
210
|
+
#: Storage
|
|
211
|
+
self.storage_link = storage_link
|
|
212
|
+
|
|
213
|
+
self._manager: Optional[WorkspaceManager] = workspace_group._manager
|
|
214
|
+
|
|
215
|
+
def __str__(self) -> str:
|
|
216
|
+
"""Return string representation."""
|
|
217
|
+
return vars_to_str(self)
|
|
218
|
+
|
|
219
|
+
def __repr__(self) -> str:
|
|
220
|
+
"""Return string representation."""
|
|
221
|
+
return str(self)
|
|
222
|
+
|
|
223
|
+
def create_cluster_identity(self) -> Dict[str, Any]:
|
|
224
|
+
"""Create a cluster identity."""
|
|
225
|
+
if self._manager is None:
|
|
226
|
+
raise ManagementError(
|
|
227
|
+
msg='No workspace manager is associated with this object.',
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
if not isinstance(self.catalog, IcebergGlueCatalog):
|
|
231
|
+
raise TypeError('Only Iceberg Glue catalog is supported at this time.')
|
|
232
|
+
|
|
233
|
+
if not isinstance(self.storage_link, S3Link):
|
|
234
|
+
raise TypeError('Only S3 links are supported at this time.')
|
|
235
|
+
|
|
236
|
+
out = self._manager._post(
|
|
237
|
+
f'workspaceGroups/{self.workspace_group.id}/'
|
|
238
|
+
'egress/createEgressClusterIdentity',
|
|
239
|
+
json=dict(
|
|
240
|
+
storageBucketName=re.split(
|
|
241
|
+
r'/+', self.storage_link.storage_base_url,
|
|
242
|
+
)[1],
|
|
243
|
+
glueRegion=self.catalog.region,
|
|
244
|
+
glueCatalogID=self.catalog.catalog_id,
|
|
245
|
+
),
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
return out.json()
|
|
249
|
+
|
|
250
|
+
def start(self, tags: Optional[List[str]] = None) -> 'ExportStatus':
|
|
251
|
+
"""Start the export process."""
|
|
252
|
+
if self._manager is None:
|
|
253
|
+
raise ManagementError(
|
|
254
|
+
msg='No workspace manager is associated with this object.',
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if not isinstance(self.storage_link, S3Link):
|
|
258
|
+
raise TypeError('Only S3 links are supported at this time.')
|
|
259
|
+
|
|
260
|
+
out = self._manager._post(
|
|
261
|
+
f'workspaceGroups/{self.workspace_group.id}/egress/startTableEgress',
|
|
262
|
+
json=dict(
|
|
263
|
+
databaseName=self.database,
|
|
264
|
+
tableName=self.table,
|
|
265
|
+
storageLocation=self.storage_link.to_storage_location(),
|
|
266
|
+
catalogInfo=self.catalog.to_catalog_info(),
|
|
267
|
+
),
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
return ExportStatus(out.json()['egressID'], self.workspace_group)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class ExportStatus(object):
|
|
274
|
+
|
|
275
|
+
export_id: str
|
|
276
|
+
|
|
277
|
+
def __init__(self, export_id: str, workspace_group: WorkspaceGroup):
|
|
278
|
+
self.export_id = export_id
|
|
279
|
+
self.workspace_group = workspace_group
|
|
280
|
+
self._manager: Optional[WorkspaceManager] = workspace_group._manager
|
|
281
|
+
|
|
282
|
+
def _info(self) -> Dict[str, Any]:
|
|
283
|
+
"""Return export status."""
|
|
284
|
+
if self._manager is None:
|
|
285
|
+
raise ManagementError(
|
|
286
|
+
msg='No workspace manager is associated with this object.',
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
out = self._manager._post(
|
|
290
|
+
f'workspaceGroups/{self.workspace_group.id}/egress/tableEgressStatus',
|
|
291
|
+
json=dict(egressID=self.export_id),
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
return out.json()
|
|
295
|
+
|
|
296
|
+
@property
|
|
297
|
+
def status(self) -> str:
|
|
298
|
+
"""Return export status."""
|
|
299
|
+
return self._info().get('status', 'Unknown')
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def message(self) -> str:
|
|
303
|
+
"""Return export status message."""
|
|
304
|
+
return self._info().get('statusMsg', '')
|
|
305
|
+
|
|
306
|
+
def __str__(self) -> str:
|
|
307
|
+
return self.status
|
|
308
|
+
|
|
309
|
+
def __repr__(self) -> str:
|
|
310
|
+
return self.status
|
singlestoredb/py.typed
ADDED
|
File without changes
|
|
@@ -465,10 +465,6 @@ class TestWorkspaceFusion(unittest.TestCase):
|
|
|
465
465
|
pass
|
|
466
466
|
|
|
467
467
|
|
|
468
|
-
@unittest.skipIf(
|
|
469
|
-
os.environ.get('SINGLESTOREDB_FUSION_ENABLE_HIDDEN', '0') == '0',
|
|
470
|
-
'Hidden Fusion commands are not enabled.',
|
|
471
|
-
)
|
|
472
468
|
@pytest.mark.management
|
|
473
469
|
class TestJobsFusion(unittest.TestCase):
|
|
474
470
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
_singlestoredb_accel.pyd,sha256=
|
|
2
|
-
singlestoredb/__init__.py,sha256=
|
|
1
|
+
_singlestoredb_accel.pyd,sha256=171M2RAd2r65yxD9_Yx-5PW3HWkw6hI5bCs99qKUrWI,59392
|
|
2
|
+
singlestoredb/__init__.py,sha256=YzFLtsA1I_g42ghOHzVvbTkNTFzBTqkclAYTShE_N3M,1697
|
|
3
3
|
singlestoredb/auth.py,sha256=RmYiH0Wlc2RXc4pTlRMysxtBI445ggCIwojWKC_eDLE,7844
|
|
4
4
|
singlestoredb/config.py,sha256=LlrwKor_23uA9u7jWBYb-IaOKs30CjWIM7o9xCXEPcc,12721
|
|
5
5
|
singlestoredb/connection.py,sha256=QC5YQemwJOhdW_-ZBFpNLE15xFxwI1fB2LuvE1hNi9k,46812
|
|
6
6
|
singlestoredb/converters.py,sha256=7_Of1f3Ow-JUoY1pHFlMPYxvt8llzdk-7X8qk5z2xJM,21631
|
|
7
7
|
singlestoredb/exceptions.py,sha256=WCCJrNSsU-hD-621Jpd6bwmvGftQ7byXkk-XKXlaxpg,3354
|
|
8
|
+
singlestoredb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
9
|
singlestoredb/pytest.py,sha256=TH364xRCN7_QaN0oRQDHixrEcDx_ZBgu3bmY0tvKrYU,9357
|
|
9
10
|
singlestoredb/types.py,sha256=Lv0BEQl6aSZBiAe0OSI07FEJhcHZ9HX45iT9NU_mxHQ,10334
|
|
10
11
|
singlestoredb/ai/__init__.py,sha256=nT048t90xqjaNhz7KJ10KfSVW4RcZRoujyC6po6Nmb8,61
|
|
@@ -31,11 +32,12 @@ singlestoredb/functions/ext/rowdat_1.py,sha256=KYj_y5JWm3_B2-QC47HK-CNOrzujBqGUw
|
|
|
31
32
|
singlestoredb/functions/ext/utils.py,sha256=OPMFD-tTCx2Kk9jguQkrTr7e4AgNkt15YsvaT1YSmN8,5480
|
|
32
33
|
singlestoredb/fusion/__init__.py,sha256=FHWtrg6OJFTf6Ye197V5sU6ssryr2h6FBcDIgXP7-H4,367
|
|
33
34
|
singlestoredb/fusion/graphql.py,sha256=SHqsPe4xgawdsTPHEtJGQlybYGWqPrGMmyK-v20RLac,5420
|
|
34
|
-
singlestoredb/fusion/handler.py,sha256=
|
|
35
|
+
singlestoredb/fusion/handler.py,sha256=KqBqssL7KVXxQWPw9bG8H8zto4ac76qEeExteBr-t1c,28437
|
|
35
36
|
singlestoredb/fusion/registry.py,sha256=_eT1gd38VPlFKs5f9Pu6lqQyoDQ_ixW5O56QwYLQ89Y,6361
|
|
36
37
|
singlestoredb/fusion/result.py,sha256=EcFY5Qv43ySlQsfl_JB-I3ko7PzVdjuhhoKN96uHSAM,12171
|
|
37
38
|
singlestoredb/fusion/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
singlestoredb/fusion/handlers/
|
|
39
|
+
singlestoredb/fusion/handlers/export.py,sha256=UCsAXOaZaQ1cD4NkslVBQ_sgzMGeZvemi-cTHcU9pBw,7342
|
|
40
|
+
singlestoredb/fusion/handlers/job.py,sha256=3enfxHwERH7T4u0FEwOPN0IL0GtepaCYgEsisiy3Df4,21753
|
|
39
41
|
singlestoredb/fusion/handlers/stage.py,sha256=uPqawMvchnpyrPYLhB0joTremCURNYKOvYntFc3zTRU,14133
|
|
40
42
|
singlestoredb/fusion/handlers/utils.py,sha256=7xWb_1mJzxW0po9iHVY2ZVnRvHIQgOlKZQZ1zllJBjk,5271
|
|
41
43
|
singlestoredb/fusion/handlers/workspace.py,sha256=NxoEY5xd5lCQmXiim4nhAYCL0agHo1H_rGPpqa31hiw,28397
|
|
@@ -44,6 +46,7 @@ singlestoredb/http/connection.py,sha256=LFUeWx7maS7xhQLqEX3pgvIGoosqyJTovtWwJ1Dy
|
|
|
44
46
|
singlestoredb/management/__init__.py,sha256=pQbsOffl-i6zIzR63MCxSjxPY6TNmy7Kg0BCTExt3mk,244
|
|
45
47
|
singlestoredb/management/billing_usage.py,sha256=0UHFSPCrN0nyeGFFM-HXS3NP8pYmYo2BCCahDEPXvzg,3883
|
|
46
48
|
singlestoredb/management/cluster.py,sha256=XfdBuTlrAG-mnW1BFKeoAr4YSE5IVgxLjbuBSpqIySo,14823
|
|
49
|
+
singlestoredb/management/export.py,sha256=PPasIoAvsdsylq1iFy1Hq6JfRSpqIuD4cZZXxUYqGEU,8968
|
|
47
50
|
singlestoredb/management/job.py,sha256=Npfe1JLYJlggGBrXLniPKwKUKF1i3alvSY1SFtvauSs,25498
|
|
48
51
|
singlestoredb/management/manager.py,sha256=uGNrUe3zhuP-HUqdfwvy4MdEXTCmq-FZKjIwZSc3hOM,9096
|
|
49
52
|
singlestoredb/management/organization.py,sha256=JBsNC4R3boUKdYvyCZyfGoVMC1mD6SPuMI1UssBVoOM,5611
|
|
@@ -106,7 +109,7 @@ singlestoredb/tests/test_dbapi.py,sha256=cNJoTEZvYG7ckcwT7xqlkJX-2TDEYGTDDU1Iguc
|
|
|
106
109
|
singlestoredb/tests/test_exceptions.py,sha256=vscMYmdOJr0JmkTAJrNI2w0Q96Nfugjkrt5_lYnw8i0,1176
|
|
107
110
|
singlestoredb/tests/test_ext_func.py,sha256=gQErR-wAN8BqLNG5U4pNbg4qkQEo6Re8Hd9_Ztqo1RM,38550
|
|
108
111
|
singlestoredb/tests/test_ext_func_data.py,sha256=9kn8BWmCjkbnP6hSbFhmhcdW4OmVT-GSvBTIzFBLEys,48796
|
|
109
|
-
singlestoredb/tests/test_fusion.py,sha256=
|
|
112
|
+
singlestoredb/tests/test_fusion.py,sha256=ckATjXKcDBvej68PZgTRJirdoywtUJ7WXkZmfR0la_4,24544
|
|
110
113
|
singlestoredb/tests/test_http.py,sha256=7hwXe61hlUes3nji0MTTZweo94tJAlJ-vA5ct9geXFQ,8868
|
|
111
114
|
singlestoredb/tests/test_management.py,sha256=B4NkK7J0luuS7T-7OR5qzu-v8gkViIiXie-58bHQIDQ,35334
|
|
112
115
|
singlestoredb/tests/test_plugin.py,sha256=P1nXLnTafaHkHN-6bVbGryxTu7OWJPU9SYFZ_WQUwq8,845
|
|
@@ -125,9 +128,9 @@ singlestoredb/utils/events.py,sha256=rC9cHAetua_E1f-EiFkFM-gJzQSQIH5Uk-4sspC3KjI
|
|
|
125
128
|
singlestoredb/utils/mogrify.py,sha256=gCcn99-vgsGVjTUV7RHJ6hH4vCNrsGB_Xo4z8kiSPDQ,4201
|
|
126
129
|
singlestoredb/utils/results.py,sha256=wR70LhCqlobniZf52r67zYLBOKjWHQm68NAskdRQND8,15862
|
|
127
130
|
singlestoredb/utils/xdict.py,sha256=-wi1lSPTnY99fhVMBhPKJ8cCsQhNG4GMUfkEBDKYgCw,13321
|
|
128
|
-
singlestoredb-1.
|
|
129
|
-
singlestoredb-1.
|
|
130
|
-
singlestoredb-1.
|
|
131
|
-
singlestoredb-1.
|
|
132
|
-
singlestoredb-1.
|
|
133
|
-
singlestoredb-1.
|
|
131
|
+
singlestoredb-1.8.0.dist-info/LICENSE,sha256=Bojenzui8aPNjlF3w4ojguDP7sTf8vFV_9Gc2UAG1sg,11542
|
|
132
|
+
singlestoredb-1.8.0.dist-info/METADATA,sha256=Y5VQXcKumZcpjgYh4pPjA4d28O5HP5BQpjNwzYyjYI8,5710
|
|
133
|
+
singlestoredb-1.8.0.dist-info/WHEEL,sha256=UyMHzmWA0xVqVPKfTiLs2eN3OWWZUl-kQemNbpIqlKo,100
|
|
134
|
+
singlestoredb-1.8.0.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
|
|
135
|
+
singlestoredb-1.8.0.dist-info/top_level.txt,sha256=SDtemIXf-Kp-_F2f_S6x0db33cHGOILdAEsIQZe2LZc,35
|
|
136
|
+
singlestoredb-1.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|