singlestoredb 1.2.0__py3-none-any.whl → 1.3.0__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 CHANGED
@@ -13,7 +13,7 @@ Examples
13
13
 
14
14
  """
15
15
 
16
- __version__ = '1.2.0'
16
+ __version__ = '1.3.0'
17
17
 
18
18
  from typing import Any
19
19
 
@@ -2,7 +2,10 @@
2
2
  """SingleStoreDB connections and cursors."""
3
3
  import abc
4
4
  import inspect
5
+ import io
6
+ import queue
5
7
  import re
8
+ import sys
6
9
  import warnings
7
10
  import weakref
8
11
  from collections.abc import Mapping
@@ -33,6 +36,11 @@ from .config import get_option
33
36
  from .utils.results import Description
34
37
  from .utils.results import Result
35
38
 
39
+ if sys.version_info < (3, 10):
40
+ InfileQueue = queue.Queue
41
+ else:
42
+ InfileQueue = queue.Queue[Union[bytes, str]]
43
+
36
44
 
37
45
  # DB-API settings
38
46
  apilevel = '2.0'
@@ -496,6 +504,14 @@ class Cursor(metaclass=abc.ABCMeta):
496
504
  def execute(
497
505
  self, query: str,
498
506
  args: Optional[Union[Sequence[Any], Dict[str, Any], Any]] = None,
507
+ infile_stream: Optional[ # type: ignore
508
+ Union[
509
+ io.RawIOBase,
510
+ io.TextIOBase,
511
+ Iterator[Union[bytes, str]],
512
+ InfileQueue,
513
+ ]
514
+ ] = None,
499
515
  ) -> int:
500
516
  """
501
517
  Execute a SQL statement.
@@ -510,6 +526,8 @@ class Cursor(metaclass=abc.ABCMeta):
510
526
  The SQL statement to execute
511
527
  args : Sequence or dict, optional
512
528
  Parameters to substitute into the SQL code
529
+ infile_stream : io.RawIOBase or io.TextIOBase or Iterator[bytes|str], optional
530
+ Data stream for ``LOCAL INFILE`` statement
513
531
 
514
532
  Examples
515
533
  --------
@@ -1339,7 +1357,8 @@ def connect(
1339
1357
  autocommit : bool, optional
1340
1358
  Enable autocommits
1341
1359
  results_type : str, optional
1342
- The form of the query results: tuples, namedtuples, dicts
1360
+ The form of the query results: tuples, namedtuples, dicts,
1361
+ numpy, polars, pandas, arrow
1343
1362
  results_format : str, optional
1344
1363
  Deprecated. This option has been renamed to results_type.
1345
1364
  program_name : str, optional
@@ -3,6 +3,7 @@
3
3
  import datetime
4
4
  import decimal
5
5
  import functools
6
+ import io
6
7
  import json
7
8
  import math
8
9
  import os
@@ -420,6 +421,14 @@ class Cursor(connection.Cursor):
420
421
  def execute(
421
422
  self, query: str,
422
423
  args: Optional[Union[Sequence[Any], Dict[str, Any]]] = None,
424
+ infile_stream: Optional[ # type: ignore
425
+ Union[
426
+ io.RawIOBase,
427
+ io.TextIOBase,
428
+ Iterable[Union[bytes, str]],
429
+ connection.InfileQueue,
430
+ ]
431
+ ] = None,
423
432
  ) -> int:
424
433
  """
425
434
  Execute a SQL statement.
@@ -432,7 +441,7 @@ class Cursor(connection.Cursor):
432
441
  Parameters to substitute into the SQL code
433
442
 
434
443
  """
435
- return self._execute(query, args)
444
+ return self._execute(query, args, infile_stream=infile_stream)
436
445
 
437
446
  def _validate_param_subs(
438
447
  self, query: str,
@@ -496,6 +505,14 @@ class Cursor(connection.Cursor):
496
505
  self, oper: str,
497
506
  params: Optional[Union[Sequence[Any], Dict[str, Any]]] = None,
498
507
  is_callproc: bool = False,
508
+ infile_stream: Optional[ # type: ignore
509
+ Union[
510
+ io.RawIOBase,
511
+ io.TextIOBase,
512
+ Iterable[Union[bytes, str]],
513
+ connection.InfileQueue,
514
+ ]
515
+ ] = None,
499
516
  ) -> int:
500
517
  self._descriptions = []
501
518
  self._schemas = []
@@ -5,12 +5,15 @@
5
5
  # https://dev.mysql.com/doc/refman/5.5/en/error-handling.html
6
6
  import errno
7
7
  import functools
8
+ import io
8
9
  import os
10
+ import queue
9
11
  import socket
10
12
  import struct
11
13
  import sys
12
14
  import traceback
13
15
  import warnings
16
+ from typing import Iterable
14
17
 
15
18
  try:
16
19
  import _singlestoredb_accel
@@ -353,6 +356,7 @@ class Connection(BaseConnection):
353
356
  )
354
357
 
355
358
  self._local_infile = bool(local_infile)
359
+ self._local_infile_stream = None
356
360
  if self._local_infile:
357
361
  client_flag |= CLIENT.LOCAL_FILES
358
362
  if multi_statements:
@@ -843,7 +847,7 @@ class Connection(BaseConnection):
843
847
  return self.cursorclass(self)
844
848
 
845
849
  # The following methods are INTERNAL USE ONLY (called from Cursor)
846
- def query(self, sql, unbuffered=False):
850
+ def query(self, sql, unbuffered=False, infile_stream=None):
847
851
  """
848
852
  Run a query on the server.
849
853
 
@@ -859,8 +863,10 @@ class Connection(BaseConnection):
859
863
  else:
860
864
  if isinstance(sql, str):
861
865
  sql = sql.encode(self.encoding, 'surrogateescape')
866
+ self._local_infile_stream = infile_stream
862
867
  self._execute_command(COMMAND.COM_QUERY, sql)
863
868
  self._affected_rows = self._read_query_result(unbuffered=unbuffered)
869
+ self._local_infile_stream = None
864
870
  return self._affected_rows
865
871
 
866
872
  def next_result(self, unbuffered=False):
@@ -1871,24 +1877,82 @@ class LoadLocalFile:
1871
1877
  def send_data(self):
1872
1878
  """Send data packets from the local file to the server"""
1873
1879
  if not self.connection._sock:
1874
- raise err.InterfaceError(0, '')
1880
+ raise err.InterfaceError(0, 'Connection is closed')
1881
+
1875
1882
  conn = self.connection
1883
+ infile = conn._local_infile_stream
1884
+
1885
+ # 16KB is efficient enough
1886
+ packet_size = min(conn.max_allowed_packet, 16 * 1024)
1876
1887
 
1877
1888
  try:
1878
- with open(self.filename, 'rb') as open_file:
1879
- packet_size = min(
1880
- conn.max_allowed_packet, 16 * 1024,
1881
- ) # 16KB is efficient enough
1882
- while True:
1883
- chunk = open_file.read(packet_size)
1884
- if not chunk:
1885
- break
1886
- conn.write_packet(chunk)
1887
- except OSError:
1888
- raise err.OperationalError(
1889
- ER.FILE_NOT_FOUND,
1890
- f"Can't find file '{self.filename}'",
1891
- )
1889
+
1890
+ if self.filename in [':stream:', b':stream:']:
1891
+
1892
+ if infile is None:
1893
+ raise err.OperationalError(
1894
+ ER.FILE_NOT_FOUND,
1895
+ ':stream: specified for LOCAL INFILE, but no stream was supplied',
1896
+ )
1897
+
1898
+ # Binary IO
1899
+ elif isinstance(infile, io.RawIOBase):
1900
+ while True:
1901
+ chunk = infile.read(packet_size)
1902
+ if not chunk:
1903
+ break
1904
+ conn.write_packet(chunk)
1905
+
1906
+ # Text IO
1907
+ elif isinstance(infile, io.TextIOBase):
1908
+ while True:
1909
+ chunk = infile.read(packet_size)
1910
+ if not chunk:
1911
+ break
1912
+ conn.write_packet(chunk.encode('utf8'))
1913
+
1914
+ # Iterable of bytes or str
1915
+ elif isinstance(infile, Iterable):
1916
+ for chunk in infile:
1917
+ if not chunk:
1918
+ continue
1919
+ if isinstance(chunk, str):
1920
+ conn.write_packet(chunk.encode('utf8'))
1921
+ else:
1922
+ conn.write_packet(chunk)
1923
+
1924
+ # Queue (empty value ends the iteration)
1925
+ elif isinstance(infile, queue.Queue):
1926
+ while True:
1927
+ chunk = infile.get()
1928
+ if not chunk:
1929
+ break
1930
+ if isinstance(chunk, str):
1931
+ conn.write_packet(chunk.encode('utf8'))
1932
+ else:
1933
+ conn.write_packet(chunk)
1934
+
1935
+ else:
1936
+ raise err.OperationalError(
1937
+ ER.FILE_NOT_FOUND,
1938
+ ':stream: specified for LOCAL INFILE, ' +
1939
+ f'but stream type is unrecognized: {infile}',
1940
+ )
1941
+
1942
+ else:
1943
+ try:
1944
+ with open(self.filename, 'rb') as open_file:
1945
+ while True:
1946
+ chunk = open_file.read(packet_size)
1947
+ if not chunk:
1948
+ break
1949
+ conn.write_packet(chunk)
1950
+ except OSError:
1951
+ raise err.OperationalError(
1952
+ ER.FILE_NOT_FOUND,
1953
+ f"Can't find file '{self.filename!s}'",
1954
+ )
1955
+
1892
1956
  finally:
1893
1957
  if not conn._closed:
1894
1958
  # send the empty packet to signify we are done sending data
@@ -178,7 +178,7 @@ class Cursor(BaseCursor):
178
178
 
179
179
  return query
180
180
 
181
- def execute(self, query, args=None):
181
+ def execute(self, query, args=None, infile_stream=None):
182
182
  """
183
183
  Execute a query.
184
184
 
@@ -192,6 +192,8 @@ class Cursor(BaseCursor):
192
192
  Query to execute.
193
193
  args : Sequence[Any] or Dict[str, Any] or Any, optional
194
194
  Parameters used with query. (optional)
195
+ infile_stream : io.BytesIO or Iterator[bytes], optional
196
+ Data stream for ``LOCAL INFILE`` statements
195
197
 
196
198
  Returns
197
199
  -------
@@ -205,7 +207,7 @@ class Cursor(BaseCursor):
205
207
 
206
208
  query = self.mogrify(query, args)
207
209
 
208
- result = self._query(query)
210
+ result = self._query(query, infile_stream=infile_stream)
209
211
  self._executed = query
210
212
  return result
211
213
 
@@ -387,10 +389,10 @@ class Cursor(BaseCursor):
387
389
  raise IndexError('out of range')
388
390
  self._rownumber = r
389
391
 
390
- def _query(self, q):
392
+ def _query(self, q, infile_stream=None):
391
393
  conn = self._get_db()
392
394
  self._clear_result()
393
- conn.query(q)
395
+ conn.query(q, infile_stream=infile_stream)
394
396
  self._do_get_result()
395
397
  return self.rowcount
396
398
 
@@ -680,10 +682,10 @@ class SSCursor(Cursor):
680
682
 
681
683
  __del__ = close
682
684
 
683
- def _query(self, q):
685
+ def _query(self, q, infile_stream=None):
684
686
  conn = self._get_db()
685
687
  self._clear_result()
686
- conn.query(q, unbuffered=True)
688
+ conn.query(q, unbuffered=True, infile_stream=infile_stream)
687
689
  self._do_get_result()
688
690
  return self.rowcount
689
691
 
@@ -515,6 +515,8 @@ _schema_converters: Dict[
515
515
  'namedtuples': _no_schema,
516
516
  'dict': _no_schema,
517
517
  'dicts': _no_schema,
518
+ 'structsequence': _no_schema,
519
+ 'structsequences': _no_schema,
518
520
  'numpy': _description_to_numpy_schema,
519
521
  'pandas': _description_to_numpy_schema,
520
522
  'polars': _description_to_polars_schema,
@@ -578,4 +580,6 @@ def get_schema(
578
580
  for the given format type
579
581
 
580
582
  """
581
- return _schema_converters[format](desc) or {}
583
+ if format in _schema_converters:
584
+ return _schema_converters[format](desc) or {}
585
+ return {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: singlestoredb
3
- Version: 1.2.0
3
+ Version: 1.3.0
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,7 +1,7 @@
1
- singlestoredb/__init__.py,sha256=rZ6NwQ45H3V4coeT30CgKFhUs7ujcmiZoca1Hw3j8_8,1634
1
+ singlestoredb/__init__.py,sha256=juu9u0zHHRDvLViF83NCU4pBRNBPEDElq5-VpoIoErg,1634
2
2
  singlestoredb/auth.py,sha256=u8D9tpKzrqa4ssaHjyZnGDX1q8XBpGtuoOkTkSv7B28,7599
3
3
  singlestoredb/config.py,sha256=H4pQxqBEGGCmVHg40VEnAdqGXHun8ougZzj-Ed6ZLH4,11822
4
- singlestoredb/connection.py,sha256=1ghx5w0VvOyf5JABD9iTC3wEJrzAsDXlzJEaIqWlsnY,44392
4
+ singlestoredb/connection.py,sha256=aLc9_9q5pJ2DCHbmBkWP-4KlDPGoCyB92yaQfNtcnSA,44958
5
5
  singlestoredb/converters.py,sha256=t1hRMZfccWJs_WyOw-W-Kh87fxsOkpOnKXAeh_Nr-zU,20681
6
6
  singlestoredb/exceptions.py,sha256=HuoA6sMRL5qiCiee-_5ddTGmFbYC9Euk8TYUsh5GvTw,3234
7
7
  singlestoredb/pytest.py,sha256=OyF3BO9mgxenifYhOihnzGk8WzCJ_zN5_mxe8XyFPOc,9074
@@ -28,7 +28,7 @@ singlestoredb/fusion/handlers/stage.py,sha256=4BSwqcmA4PI3oksTdYaTmQ6bY-AzmL0mZf
28
28
  singlestoredb/fusion/handlers/utils.py,sha256=oYbf13Y3orEkJfHMNnO7B_W1anEdK-0S9vVVkF2pPFk,5109
29
29
  singlestoredb/fusion/handlers/workspace.py,sha256=EGxu-dAWNfHsknMNSk1n9cri_zhlMJap4ypywPFiMZQ,25000
30
30
  singlestoredb/http/__init__.py,sha256=A_2ZUCCpvRYIA6YDpPy57wL5R1eZ5SfP6I1To5nfJ2s,912
31
- singlestoredb/http/connection.py,sha256=RUPTgbnrvlp_fRCgoFl0og_-35TSVAAjkxUFctO8DWw,38818
31
+ singlestoredb/http/connection.py,sha256=HUYzgiGYRUblHhgDi-ghfXykGkdB9iQ3i_XVvhtqH88,39349
32
32
  singlestoredb/management/__init__.py,sha256=puMnH8S3BWEgIlAmuLLh_ZqTN1jml7E8Mme30vkmPOQ,235
33
33
  singlestoredb/management/billing_usage.py,sha256=9ighjIpcopgIyJOktBYQ6pahBZmWGHOPyyCW4gu9FGs,3735
34
34
  singlestoredb/management/cluster.py,sha256=_TT4tV43VPDrtcdS_VN-TTYij7yFQzjAMeuYRF9zKj8,14362
@@ -40,9 +40,9 @@ singlestoredb/management/workspace.py,sha256=jHNTZ_zqi80U20BS6ezJX5DLZxWX_68fV1G
40
40
  singlestoredb/mysql/__init__.py,sha256=olUTAvkiERhDW41JXQMawkg-i0tvBEkoTkII1tt6lxU,4492
41
41
  singlestoredb/mysql/_auth.py,sha256=AugRitoUwgRIDFuJxuAH4MWIAmckY7Ji2pP6r_Ng9dY,8043
42
42
  singlestoredb/mysql/charset.py,sha256=-FlONDS_oAUF5B3mIgeHBPb_SCt4zHD33arUeBNctU0,10510
43
- singlestoredb/mysql/connection.py,sha256=5gXm2UxtUmAbQnp0LzRuGO4MTEX1xFDCLpKtjwj43j8,67884
43
+ singlestoredb/mysql/connection.py,sha256=WZZl3cWLsyuKlQfB3CzXRmXnAXcTLDBUG8eAViYEwDw,70301
44
44
  singlestoredb/mysql/converters.py,sha256=CVe8SDmjbIAhy1xpQ2N5OKWw6t5eWpw-EU3QTlA0Hh0,7500
45
- singlestoredb/mysql/cursors.py,sha256=Drdy3WN5SRTdAa7KQTefDobTDKCUTtto1GiwIbWfFHc,26527
45
+ singlestoredb/mysql/cursors.py,sha256=Eqe7jITRvOo4P_TxIarTumg_2PG1DcCfZ4Uo9IFdDa8,26794
46
46
  singlestoredb/mysql/err.py,sha256=-m5rqXi8yhq6b8SCEJ2h0E5Rudh_15dlAU_WbJ1YrM8,2388
47
47
  singlestoredb/mysql/optionfile.py,sha256=DqL-rOQcqQncD5eVbPRkwZqo7Pj3Vh40VLx3E_e87TU,655
48
48
  singlestoredb/mysql/protocol.py,sha256=2GG8qTXy5npqo7z2D2K5T0S8PtoUOS-hFDEXy8VConw,14451
@@ -108,11 +108,11 @@ singlestoredb/utils/convert_rows.py,sha256=A6up7a8Bq-eV2BXdGCotQviqp1Q7XdJ2MA933
108
108
  singlestoredb/utils/debug.py,sha256=0JiLA37u_9CKiDGiN9BK_PtFMUku3vIcNjERWaTNRSU,349
109
109
  singlestoredb/utils/dtypes.py,sha256=1qUiB4BJFJ7rOVh2mItQssYbJupV7uq1x8uwX-Eu2Ks,5898
110
110
  singlestoredb/utils/mogrify.py,sha256=-a56IF70U6CkfadeaZgfjRSVsAD3PuqRrzPpjZlgbwY,4050
111
- singlestoredb/utils/results.py,sha256=Hs34Q35UQzPQuTZ-mwidNJVU26WlQmSNqfpKwQ2XUfc,15153
111
+ singlestoredb/utils/results.py,sha256=bJtaUaDiFq26IsPAKZ2FHGB7csMn94EAxLKrP4HaEEA,15277
112
112
  singlestoredb/utils/xdict.py,sha256=S9HKgrPrnu_6b7iOwa2KrW8CmU1Uqx0BWdEyogFzWbE,12896
113
- singlestoredb-1.2.0.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
114
- singlestoredb-1.2.0.dist-info/METADATA,sha256=UBRxeu9w0pspytg7fNSvYhCbTglBsDtvje-nEKczlao,5570
115
- singlestoredb-1.2.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
116
- singlestoredb-1.2.0.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
117
- singlestoredb-1.2.0.dist-info/top_level.txt,sha256=eet8bVPNRqiGeY0PrO5ERH2UpamwlrKHEQCffz4dOh8,14
118
- singlestoredb-1.2.0.dist-info/RECORD,,
113
+ singlestoredb-1.3.0.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
114
+ singlestoredb-1.3.0.dist-info/METADATA,sha256=PLBzts-ZoiYv7LrQvRGSKycJ9DJ7W2KsnLMDkFJ_1LA,5570
115
+ singlestoredb-1.3.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
116
+ singlestoredb-1.3.0.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
117
+ singlestoredb-1.3.0.dist-info/top_level.txt,sha256=eet8bVPNRqiGeY0PrO5ERH2UpamwlrKHEQCffz4dOh8,14
118
+ singlestoredb-1.3.0.dist-info/RECORD,,