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.

Binary file
singlestoredb/__init__.py CHANGED
@@ -13,7 +13,7 @@ Examples
13
13
 
14
14
  """
15
15
 
16
- __version__ = '0.9.2'
16
+ __version__ = '0.9.3'
17
17
 
18
18
  from .alchemy import create_engine
19
19
  from .config import options, get_option, set_option, describe_option
@@ -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"-?\d+(\.\d+)?|-?\.d+"
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+|$))+', grammar)
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
- res = self.run(self.visit(type(self).grammar.parse(sql)))
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
- return result.FusionSQLResult(self.connection)
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: self.validate_rule(node.expr_name, out)}
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():
@@ -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()
@@ -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(converters)
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,6 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
  # type: ignore
3
- """SingleStoreDB HTTP connection testing."""
3
+ """SingleStoreDB Management API testing."""
4
4
  import os
5
5
  import pathlib
6
6
  import random
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: singlestoredb
3
- Version: 0.9.2
3
+ Version: 0.9.3
4
4
  Summary: Interface to the SingleStoreDB database and workspace management APIs
5
5
  Home-page: https://github.com/singlestore-labs/singlestoredb-python
6
6
  Author: SingleStore
@@ -1,18 +1,18 @@
1
- _singlestoredb_accel.abi3.so,sha256=9lPUZMAFuO6P1ta_QgbpyHcAtz0lWGG2NvPeosXMu1k,155997
2
- singlestoredb-0.9.2.dist-info/RECORD,,
3
- singlestoredb-0.9.2.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
4
- singlestoredb-0.9.2.dist-info/WHEEL,sha256=L_8uSJgNH7VmiqwHcew_IFGyTcruK-C2YxnyqVh4uE0,113
5
- singlestoredb-0.9.2.dist-info/top_level.txt,sha256=SDtemIXf-Kp-_F2f_S6x0db33cHGOILdAEsIQZe2LZc,35
6
- singlestoredb-0.9.2.dist-info/METADATA,sha256=xhp-I0kwsGSTrWrxRnHV03bYfMUo9hVKHhbLx14pCUM,7638
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=8Q63hlVGXnEzxWSEn1n_jrM6VMRwXYdYdMY6bvPz5IQ,877
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=Jzo04_ps9EJh-Q5qVmwuornB5-dKCqHPAg0WHJyYAM8,15598
15
- singlestoredb/fusion/registry.py,sha256=ROQNH3azs_F-LbBMt8BjgeZwfo85wCM9tu9tsy0tTJc,2948
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=v3oi63-HpdhBb9Ez0pVqN_xBU--jniwlA42npef3sPY,25725
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=JGXTzG5ysfnRoGf2fkck8GjQcfwUDOyxmeZeJwKhIOA,33979
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