singlestoredb 0.9.2__cp36-abi3-macosx_10_9_universal2.whl → 0.9.3__cp36-abi3-macosx_10_9_universal2.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.abi3.so +0 -0
- singlestoredb/__init__.py +1 -1
- singlestoredb/fusion/handler.py +27 -14
- singlestoredb/fusion/registry.py +55 -0
- singlestoredb/http/connection.py +4 -1
- singlestoredb/tests/test_fusion.py +83 -0
- singlestoredb/tests/test_management.py +1 -1
- {singlestoredb-0.9.2.dist-info → singlestoredb-0.9.3.dist-info}/METADATA +1 -1
- {singlestoredb-0.9.2.dist-info → singlestoredb-0.9.3.dist-info}/RECORD +12 -11
- {singlestoredb-0.9.2.dist-info → singlestoredb-0.9.3.dist-info}/LICENSE +0 -0
- {singlestoredb-0.9.2.dist-info → singlestoredb-0.9.3.dist-info}/WHEEL +0 -0
- {singlestoredb-0.9.2.dist-info → singlestoredb-0.9.3.dist-info}/top_level.txt +0 -0
_singlestoredb_accel.abi3.so
CHANGED
|
Binary file
|
singlestoredb/__init__.py
CHANGED
singlestoredb/fusion/handler.py
CHANGED
|
@@ -21,8 +21,8 @@ from ..connection import Connection
|
|
|
21
21
|
|
|
22
22
|
CORE_GRAMMAR = r'''
|
|
23
23
|
ws = ~r"(\s*(/\*.*\*/)*\s*)*"
|
|
24
|
-
qs = ~r"\"([^\"]*)\"|'([^\']*)'"
|
|
25
|
-
number = ~r"
|
|
24
|
+
qs = ~r"\"([^\"]*)\"|'([^\']*)'|`([^\`]*)`|(\S+)"
|
|
25
|
+
number = ~r"[-+]?(\d*\.)?\d+(e[-+]?\d+)?"i
|
|
26
26
|
integer = ~r"-?\d+"
|
|
27
27
|
comma = ws "," ws
|
|
28
28
|
open_paren = ws "(" ws
|
|
@@ -32,10 +32,10 @@ CORE_GRAMMAR = r'''
|
|
|
32
32
|
|
|
33
33
|
def get_keywords(grammar: str) -> Tuple[str, ...]:
|
|
34
34
|
"""Return all all-caps words from the beginning of the line."""
|
|
35
|
-
m = re.match(r'^\s*([A-Z0-9_]+(\s
|
|
35
|
+
m = re.match(r'^\s*([A-Z0-9_]+(\s+|$|;))+', grammar)
|
|
36
36
|
if not m:
|
|
37
37
|
return tuple()
|
|
38
|
-
return tuple(re.split(r'\s+', m.group(0).strip()))
|
|
38
|
+
return tuple(re.split(r'\s+', m.group(0).replace(';', '').strip()))
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def process_optional(m: Any) -> str:
|
|
@@ -322,6 +322,7 @@ class SQLHandler(NodeVisitor):
|
|
|
322
322
|
#: Rule validation functions
|
|
323
323
|
validators: Dict[str, Callable[..., Any]] = {}
|
|
324
324
|
|
|
325
|
+
_grammar: str = CORE_GRAMMAR
|
|
325
326
|
_is_compiled: bool = False
|
|
326
327
|
|
|
327
328
|
def __init__(self, connection: Connection):
|
|
@@ -347,6 +348,7 @@ class SQLHandler(NodeVisitor):
|
|
|
347
348
|
cls.grammar, cls.command_key, cls.rule_info, cls.help = \
|
|
348
349
|
process_grammar(grammar or cls.__doc__ or '')
|
|
349
350
|
|
|
351
|
+
cls._grammar = grammar or cls.__doc__ or ''
|
|
350
352
|
cls._is_compiled = True
|
|
351
353
|
|
|
352
354
|
def create_result(self) -> result.FusionSQLResult:
|
|
@@ -384,10 +386,15 @@ class SQLHandler(NodeVisitor):
|
|
|
384
386
|
"""
|
|
385
387
|
type(self).compile()
|
|
386
388
|
try:
|
|
387
|
-
|
|
389
|
+
params = self.visit(type(self).grammar.parse(sql))
|
|
390
|
+
for k, v in params.items():
|
|
391
|
+
params[k] = self.validate_rule(k, v)
|
|
392
|
+
res = self.run(params)
|
|
388
393
|
if res is not None:
|
|
389
394
|
return res
|
|
390
|
-
|
|
395
|
+
res = result.FusionSQLResult(self.connection)
|
|
396
|
+
res.set_rows([])
|
|
397
|
+
return res
|
|
391
398
|
except ParseError as exc:
|
|
392
399
|
s = str(exc)
|
|
393
400
|
msg = ''
|
|
@@ -421,7 +428,7 @@ class SQLHandler(NodeVisitor):
|
|
|
421
428
|
"""
|
|
422
429
|
raise NotImplementedError
|
|
423
430
|
|
|
424
|
-
def create_like_func(self, like: str) -> Callable[[str], bool]:
|
|
431
|
+
def create_like_func(self, like: Optional[str]) -> Callable[[str], bool]:
|
|
425
432
|
"""
|
|
426
433
|
Construct a function to apply the LIKE clause.
|
|
427
434
|
|
|
@@ -457,7 +464,16 @@ class SQLHandler(NodeVisitor):
|
|
|
457
464
|
"""Quoted strings."""
|
|
458
465
|
if node is None:
|
|
459
466
|
return None
|
|
460
|
-
return node.match.group(1) or node.match.group(2)
|
|
467
|
+
return node.match.group(1) or node.match.group(2) or \
|
|
468
|
+
node.match.group(3) or node.match.group(4)
|
|
469
|
+
|
|
470
|
+
def visit_number(self, node: Node, visited_children: Iterable[Any]) -> Any:
|
|
471
|
+
"""Numeric value."""
|
|
472
|
+
return float(node.match.group(0))
|
|
473
|
+
|
|
474
|
+
def visit_integer(self, node: Node, visited_children: Iterable[Any]) -> Any:
|
|
475
|
+
"""Integer value."""
|
|
476
|
+
return int(node.match.group(0))
|
|
461
477
|
|
|
462
478
|
def visit_ws(self, node: Node, visited_children: Iterable[Any]) -> Any:
|
|
463
479
|
"""Whitespace and comments."""
|
|
@@ -505,13 +521,10 @@ class SQLHandler(NodeVisitor):
|
|
|
505
521
|
# Filter out stray empty strings
|
|
506
522
|
out = [x for x in flatten(visited_children)[n_keywords:] if x]
|
|
507
523
|
|
|
508
|
-
if repeats:
|
|
509
|
-
return {node.expr_name:
|
|
524
|
+
if repeats or len(out) > 1:
|
|
525
|
+
return {node.expr_name: out}
|
|
510
526
|
|
|
511
|
-
return {
|
|
512
|
-
node.expr_name:
|
|
513
|
-
self.validate_rule(node.expr_name, out[0]) if out else True,
|
|
514
|
-
}
|
|
527
|
+
return {node.expr_name: out[0] if out else True}
|
|
515
528
|
|
|
516
529
|
if hasattr(node, 'match'):
|
|
517
530
|
if not visited_children and not node.match.groups():
|
singlestoredb/fusion/registry.py
CHANGED
|
@@ -3,7 +3,9 @@ import os
|
|
|
3
3
|
import re
|
|
4
4
|
from typing import Any
|
|
5
5
|
from typing import Dict
|
|
6
|
+
from typing import List
|
|
6
7
|
from typing import Optional
|
|
8
|
+
from typing import Tuple
|
|
7
9
|
from typing import Type
|
|
8
10
|
from typing import Union
|
|
9
11
|
|
|
@@ -110,3 +112,56 @@ def execute(
|
|
|
110
112
|
raise RuntimeError(f'could not find handler for query: {sql}')
|
|
111
113
|
|
|
112
114
|
return handler(connection).execute(sql)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class ShowFusionCommandsHandler(SQLHandler):
|
|
118
|
+
"""
|
|
119
|
+
SHOW FUSION COMMANDS [ like ];
|
|
120
|
+
|
|
121
|
+
# LIKE pattern
|
|
122
|
+
like = LIKE '<pattern>'
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
def run(self, params: Dict[str, Any]) -> Optional[result.FusionSQLResult]:
|
|
127
|
+
res = self.create_result()
|
|
128
|
+
res.add_field('Command', result.STRING)
|
|
129
|
+
|
|
130
|
+
is_like = self.create_like_func(params.get('like'))
|
|
131
|
+
|
|
132
|
+
data: List[Tuple[Any, ...]] = []
|
|
133
|
+
for _, v in sorted(_handlers.items()):
|
|
134
|
+
if v is type(self):
|
|
135
|
+
continue
|
|
136
|
+
if is_like(' '.join(v.command_key)):
|
|
137
|
+
data.append((v.help,))
|
|
138
|
+
|
|
139
|
+
res.set_rows(data)
|
|
140
|
+
|
|
141
|
+
return res
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
ShowFusionCommandsHandler.register()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ShowFusionGrammarHandler(SQLHandler):
|
|
148
|
+
"""
|
|
149
|
+
SHOW FUSION GRAMMAR for_query;
|
|
150
|
+
|
|
151
|
+
# Query to show grammar for
|
|
152
|
+
for_query = FOR '<query>'
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
def run(self, params: Dict[str, Any]) -> Optional[result.FusionSQLResult]:
|
|
157
|
+
res = self.create_result()
|
|
158
|
+
res.add_field('Grammar', result.STRING)
|
|
159
|
+
handler = get_handler(params['for_query'])
|
|
160
|
+
data: List[Tuple[Any, ...]] = []
|
|
161
|
+
if handler is not None:
|
|
162
|
+
data.append((handler._grammar,))
|
|
163
|
+
res.set_rows(data)
|
|
164
|
+
return res
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
ShowFusionGrammarHandler.register()
|
singlestoredb/http/connection.py
CHANGED
|
@@ -543,7 +543,7 @@ class Cursor(connection.Cursor):
|
|
|
543
543
|
# precision, scale, null_ok, column_flags, charset)
|
|
544
544
|
|
|
545
545
|
# Remove converters for things the JSON parser already converted
|
|
546
|
-
http_converters = dict(
|
|
546
|
+
http_converters = dict(self._connection.decoders)
|
|
547
547
|
http_converters.pop(4, None)
|
|
548
548
|
http_converters.pop(5, None)
|
|
549
549
|
http_converters.pop(6, None)
|
|
@@ -955,6 +955,9 @@ class Connection(connection.Connection):
|
|
|
955
955
|
version = kwargs.get('version', 'v2')
|
|
956
956
|
self.driver = kwargs.get('driver', 'https')
|
|
957
957
|
|
|
958
|
+
self.encoders = {k: v for (k, v) in converters.items() if type(k) is not int}
|
|
959
|
+
self.decoders = {k: v for (k, v) in converters.items() if type(k) is int}
|
|
960
|
+
|
|
958
961
|
self._database = kwargs.get('database', get_option('database'))
|
|
959
962
|
self._url = f'{self.driver}://{host}:{port}/api/{version}/'
|
|
960
963
|
self._messages: List[Tuple[int, str]] = []
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# type: ignore
|
|
3
|
+
"""SingleStoreDB Fusion testing."""
|
|
4
|
+
import os
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
import singlestoredb as s2
|
|
8
|
+
from singlestoredb.tests import utils
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestFusion(unittest.TestCase):
|
|
12
|
+
|
|
13
|
+
dbname: str = ''
|
|
14
|
+
dbexisted: bool = False
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def setUpClass(cls):
|
|
18
|
+
sql_file = os.path.join(os.path.dirname(__file__), 'test.sql')
|
|
19
|
+
cls.dbname, cls.dbexisted = utils.load_sql(sql_file)
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def tearDownClass(cls):
|
|
23
|
+
if not cls.dbexisted:
|
|
24
|
+
utils.drop_database(cls.dbname)
|
|
25
|
+
|
|
26
|
+
def setUp(self):
|
|
27
|
+
self.enabled = os.environ.get('SINGLESTOREDB_ENABLE_FUSION')
|
|
28
|
+
os.environ['SINGLESTOREDB_ENABLE_FUSION'] = '1'
|
|
29
|
+
self.conn = s2.connect(database=type(self).dbname, local_infile=True)
|
|
30
|
+
self.cur = self.conn.cursor()
|
|
31
|
+
|
|
32
|
+
def tearDown(self):
|
|
33
|
+
if self.enabled:
|
|
34
|
+
os.environ['SINGLESTOREDB_ENABLE_FUSION'] = self.enabled
|
|
35
|
+
else:
|
|
36
|
+
del os.environ['SINGLESTOREDB_ENABLE_FUSION']
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
if self.cur is not None:
|
|
40
|
+
self.cur.close()
|
|
41
|
+
except Exception:
|
|
42
|
+
# traceback.print_exc()
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
if self.conn is not None:
|
|
47
|
+
self.conn.close()
|
|
48
|
+
except Exception:
|
|
49
|
+
# traceback.print_exc()
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
def test_env_var(self):
|
|
53
|
+
os.environ['SINGLESTOREDB_ENABLE_FUSION'] = '0'
|
|
54
|
+
|
|
55
|
+
with self.assertRaises(s2.ProgrammingError):
|
|
56
|
+
self.cur.execute('show fusion commands')
|
|
57
|
+
|
|
58
|
+
del os.environ['SINGLESTOREDB_ENABLE_FUSION']
|
|
59
|
+
|
|
60
|
+
with self.assertRaises(s2.ProgrammingError):
|
|
61
|
+
self.cur.execute('show fusion commands')
|
|
62
|
+
|
|
63
|
+
os.environ['SINGLESTOREDB_ENABLE_FUSION'] = 'yes'
|
|
64
|
+
|
|
65
|
+
self.cur.execute('show fusion commands')
|
|
66
|
+
assert list(self.cur)
|
|
67
|
+
|
|
68
|
+
def test_show_commands(self):
|
|
69
|
+
self.cur.execute('show fusion commands')
|
|
70
|
+
cmds = [x[0] for x in self.cur.fetchall()]
|
|
71
|
+
assert cmds
|
|
72
|
+
assert [x for x in cmds if x.strip().startswith('SHOW FUSION GRAMMAR')], cmds
|
|
73
|
+
|
|
74
|
+
self.cur.execute('show fusion commands like "create%"')
|
|
75
|
+
cmds = [x[0] for x in self.cur.fetchall()]
|
|
76
|
+
assert cmds
|
|
77
|
+
assert [x for x in cmds if x.strip().startswith('CREATE')] == cmds, cmds
|
|
78
|
+
|
|
79
|
+
def test_show_grammar(self):
|
|
80
|
+
self.cur.execute('show fusion grammar for "create workspace"')
|
|
81
|
+
cmds = [x[0] for x in self.cur.fetchall()]
|
|
82
|
+
assert cmds
|
|
83
|
+
assert [x for x in cmds if x.strip().startswith('CREATE WORKSPACE')], cmds
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
_singlestoredb_accel.abi3.so,sha256=
|
|
2
|
-
singlestoredb-0.9.
|
|
3
|
-
singlestoredb-0.9.
|
|
4
|
-
singlestoredb-0.9.
|
|
5
|
-
singlestoredb-0.9.
|
|
6
|
-
singlestoredb-0.9.
|
|
1
|
+
_singlestoredb_accel.abi3.so,sha256=VIOjqjUMsueOx8deGP6b-mWB7seO_mZkjl1uVZBvqa0,155997
|
|
2
|
+
singlestoredb-0.9.3.dist-info/RECORD,,
|
|
3
|
+
singlestoredb-0.9.3.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
|
|
4
|
+
singlestoredb-0.9.3.dist-info/WHEEL,sha256=L_8uSJgNH7VmiqwHcew_IFGyTcruK-C2YxnyqVh4uE0,113
|
|
5
|
+
singlestoredb-0.9.3.dist-info/top_level.txt,sha256=SDtemIXf-Kp-_F2f_S6x0db33cHGOILdAEsIQZe2LZc,35
|
|
6
|
+
singlestoredb-0.9.3.dist-info/METADATA,sha256=1Ij3MmwwNUyUdji1btNWArhmGOqntKd0Lrkf-7i3w48,7638
|
|
7
7
|
singlestoredb/auth.py,sha256=u8D9tpKzrqa4ssaHjyZnGDX1q8XBpGtuoOkTkSv7B28,7599
|
|
8
8
|
singlestoredb/config.py,sha256=TLiXGoIZhYRYALY0t7_nFpiKf_yhyljXZhED4i0FHlU,7139
|
|
9
|
-
singlestoredb/__init__.py,sha256=
|
|
9
|
+
singlestoredb/__init__.py,sha256=Av8l6xYIXKaV0oS7gIN4yHQ-OmzLE15NaUKLDlGYKJA,877
|
|
10
10
|
singlestoredb/types.py,sha256=FIqO1A7e0Gkk7ITmIysBy-P5S--ItbMSlYvblzqGS30,9969
|
|
11
11
|
singlestoredb/connection.py,sha256=FIo-O_OpM2RD1wsONWTH1kHQNi__v6vMsS4ns0e5j_0,43985
|
|
12
12
|
singlestoredb/exceptions.py,sha256=HuoA6sMRL5qiCiee-_5ddTGmFbYC9Euk8TYUsh5GvTw,3234
|
|
13
13
|
singlestoredb/converters.py,sha256=aH_QhLr94i9_AjvcplTar0HfX2yi53KGxHHzJc7lSU0,12515
|
|
14
|
-
singlestoredb/fusion/handler.py,sha256=
|
|
15
|
-
singlestoredb/fusion/registry.py,sha256=
|
|
14
|
+
singlestoredb/fusion/handler.py,sha256=Z-gJyhFlx6m56i4A3m77dtP3SvsxT7JZ6ZvpyiVyMpc,16170
|
|
15
|
+
singlestoredb/fusion/registry.py,sha256=9IhZAuPUw8zKWhN26CWrm-pJi6wlGEYzs_Alpf9XjVg,4266
|
|
16
16
|
singlestoredb/fusion/__init__.py,sha256=Qo7SuqGw-l-vE8-EI2jhm6hXJkYfOLUKIws9c7LFNX0,356
|
|
17
17
|
singlestoredb/fusion/result.py,sha256=8kEehzvJp0g7OyaDJfNT1qXbp1b6tBeRMcQPQo2GB48,3899
|
|
18
18
|
singlestoredb/fusion/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -20,11 +20,12 @@ singlestoredb/fusion/handlers/workspace.py,sha256=0X_xbP9KnW4HJ4lVshhtTcyjbGfvoy
|
|
|
20
20
|
singlestoredb/tests/test.sql,sha256=wtHioW6SNa2nht71Bo9XQkE36-X1L6C_AZ5QyMJuvqM,9415
|
|
21
21
|
singlestoredb/tests/test_xdict.py,sha256=fqHspoi39nbX3fIDVkkRXcd5H50xdOsSvK0bxAMQnaE,10408
|
|
22
22
|
singlestoredb/tests/test_results.py,sha256=wg93sujwt-R9_eJCgSCElgAZhLDkIiAo3qPkPydOv78,6582
|
|
23
|
+
singlestoredb/tests/test_fusion.py,sha256=sfCVjhCFJiHPTROX3ynwuKn9oQKVpaJFzFa5-DP-jGw,2541
|
|
23
24
|
singlestoredb/tests/test_basics.py,sha256=rUfUGZ54xybvgp11XYWdqnUYMKa6VckB3XkX9LFnxRw,44180
|
|
24
25
|
singlestoredb/tests/test_connection.py,sha256=RiE_NATLYPiMR5jWaBlcP5YddwitS6dzOHOVVOVXCSI,50741
|
|
25
26
|
singlestoredb/tests/test_exceptions.py,sha256=tfr_8X2w1UmG4nkSBzWGB0C7ehrf1GAVgj6_ODaG-TM,1131
|
|
26
27
|
singlestoredb/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
-
singlestoredb/tests/test_management.py,sha256=
|
|
28
|
+
singlestoredb/tests/test_management.py,sha256=XXHBz38DoAAuceWjDnVHRLNLwqXNCL-BV9SfmF3m5pc,25724
|
|
28
29
|
singlestoredb/tests/test_udf.py,sha256=l3nZsqR6QjKH4GAujy9AHjq57rUwx9FrcRl6R1k6gSM,28864
|
|
29
30
|
singlestoredb/tests/test_http.py,sha256=RXasTqBWRn__omj0eLFTJYIbZjd0PPdIV2d4Cqz0MC8,8580
|
|
30
31
|
singlestoredb/tests/utils.py,sha256=76eNdYFVnsw6S3J_RaGgGQ87Rlm8pxwyYaFYXnvAEvk,4673
|
|
@@ -50,7 +51,7 @@ singlestoredb/utils/xdict.py,sha256=S9HKgrPrnu_6b7iOwa2KrW8CmU1Uqx0BWdEyogFzWbE,
|
|
|
50
51
|
singlestoredb/utils/debug.py,sha256=0JiLA37u_9CKiDGiN9BK_PtFMUku3vIcNjERWaTNRSU,349
|
|
51
52
|
singlestoredb/utils/mogrify.py,sha256=-a56IF70U6CkfadeaZgfjRSVsAD3PuqRrzPpjZlgbwY,4050
|
|
52
53
|
singlestoredb/http/__init__.py,sha256=A_2ZUCCpvRYIA6YDpPy57wL5R1eZ5SfP6I1To5nfJ2s,912
|
|
53
|
-
singlestoredb/http/connection.py,sha256=
|
|
54
|
+
singlestoredb/http/connection.py,sha256=KwAH-UI-SU2jBDWTeweJILnsqr0p2gHxngIbsTrpjgE,34163
|
|
54
55
|
singlestoredb/mysql/protocol.py,sha256=TfG247zY7ngg03JLDzANYwpc9SIlJa_fGOeMF1lRRG0,12138
|
|
55
56
|
singlestoredb/mysql/cursors.py,sha256=aWs4AzmeZJJltOmUU3GZNBWgod9nqnnFW5OHVquz5t0,21246
|
|
56
57
|
singlestoredb/mysql/__init__.py,sha256=olUTAvkiERhDW41JXQMawkg-i0tvBEkoTkII1tt6lxU,4492
|
|
File without changes
|
|
File without changes
|
|
File without changes
|