singlestoredb 1.12.2__py3-none-any.whl → 1.12.4__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.12.2'
16
+ __version__ = '1.12.4'
17
17
 
18
18
  from typing import Any
19
19
 
singlestoredb/config.py CHANGED
@@ -80,7 +80,7 @@ register_option(
80
80
  )
81
81
 
82
82
  register_option(
83
- 'charset', 'string', check_str, 'utf8',
83
+ 'charset', 'string', check_str, 'utf8mb4',
84
84
  'Specifies the character set for the session.',
85
85
  environ='SINGLESTOREDB_CHARSET',
86
86
  )
@@ -69,6 +69,12 @@ try:
69
69
  except ImportError:
70
70
  has_cloudpickle = False
71
71
 
72
+ try:
73
+ from pydantic import BaseModel
74
+ has_pydantic = True
75
+ except ImportError:
76
+ has_pydantic = False
77
+
72
78
 
73
79
  logger = utils.get_logger('singlestoredb.functions.ext.asgi')
74
80
 
@@ -138,13 +144,24 @@ def get_func_names(funcs: str) -> List[Tuple[str, str]]:
138
144
 
139
145
 
140
146
  def as_tuple(x: Any) -> Any:
141
- if hasattr(x, 'model_fields'):
142
- return tuple(x.model_fields.values())
147
+ """Convert object to tuple."""
148
+ if has_pydantic and isinstance(x, BaseModel):
149
+ return tuple(x.model_dump().values())
143
150
  if dataclasses.is_dataclass(x):
144
151
  return dataclasses.astuple(x)
145
152
  return x
146
153
 
147
154
 
155
+ def as_list_of_tuples(x: Any) -> Any:
156
+ """Convert object to a list of tuples."""
157
+ if isinstance(x, (list, tuple)) and len(x) > 0:
158
+ if has_pydantic and isinstance(x[0], BaseModel):
159
+ return [tuple(y.model_dump().values()) for y in x]
160
+ if dataclasses.is_dataclass(x[0]):
161
+ return [dataclasses.astuple(y) for y in x]
162
+ return x
163
+
164
+
148
165
  def make_func(
149
166
  name: str,
150
167
  func: Callable[..., Any],
@@ -183,7 +200,7 @@ def make_func(
183
200
  out_ids: List[int] = []
184
201
  out = []
185
202
  for i, res in zip(row_ids, func_map(func, rows)):
186
- out.extend(as_tuple(res))
203
+ out.extend(as_list_of_tuples(res))
187
204
  out_ids.extend([row_ids[i]] * (len(out)-len(out_ids)))
188
205
  return out_ids, out
189
206
 
@@ -6,6 +6,8 @@ import numbers
6
6
  import os
7
7
  import re
8
8
  import string
9
+ import sys
10
+ import types
9
11
  import typing
10
12
  from typing import Any
11
13
  from typing import Callable
@@ -32,6 +34,11 @@ except ImportError:
32
34
  from . import dtypes as dt
33
35
  from ..mysql.converters import escape_item # type: ignore
34
36
 
37
+ if sys.version_info >= (3, 10):
38
+ _UNION_TYPES = {typing.Union, types.UnionType}
39
+ else:
40
+ _UNION_TYPES = {typing.Union}
41
+
35
42
 
36
43
  array_types: Tuple[Any, ...]
37
44
 
@@ -211,7 +218,7 @@ def simplify_dtype(dtype: Any) -> List[Any]:
211
218
  args = []
212
219
 
213
220
  # Flatten Unions
214
- if origin is Union:
221
+ if origin in _UNION_TYPES:
215
222
  for x in typing.get_args(dtype):
216
223
  args.extend(simplify_dtype(x))
217
224
 
@@ -43,6 +43,12 @@ try:
43
43
  except ImportError:
44
44
  has_shapely = False
45
45
 
46
+ try:
47
+ import pydantic
48
+ has_pydantic = True
49
+ except ImportError:
50
+ has_pydantic = False
51
+
46
52
  from .. import connection
47
53
  from .. import fusion
48
54
  from .. import types
@@ -533,6 +539,9 @@ class Cursor(connection.Cursor):
533
539
  self._expect_results = True
534
540
  sql_type = 'query'
535
541
 
542
+ if has_pydantic and isinstance(params, pydantic.BaseModel):
543
+ params = params.model_dump()
544
+
536
545
  self._validate_param_subs(oper, params)
537
546
 
538
547
  handler = fusion.get_handler(oper)
@@ -989,18 +989,11 @@ class Connection(BaseConnection):
989
989
 
990
990
  def set_character_set(self, charset, collation=None):
991
991
  """
992
- Set session charaset (and collation) on the server.
992
+ Set charaset (and collation) on the server.
993
993
 
994
- Send "SET [COLLATION|CHARACTER_SET]_SERVER = [collation|charset]" query.
994
+ Send "SET NAMES charset [COLLATE collation]" query.
995
995
  Update Connection.encoding based on charset.
996
996
 
997
- If charset/collation are being set to utf8mb4, the corresponding global
998
- variables (COLLATION_SERVER and CHARACTER_SET_SERVER) must be also set
999
- to utf8mb4. This is true by default for SingleStore 8.7+. For previuous
1000
- versions or non-default setting user must manully run the query
1001
- `SET global collation_connection = utf8mb4_general_ci`
1002
- replacing utf8mb4_general_ci with {collation}.
1003
-
1004
997
  Parameters
1005
998
  ----------
1006
999
  charset : str
@@ -1013,9 +1006,9 @@ class Connection(BaseConnection):
1013
1006
  encoding = charset_by_name(charset).encoding
1014
1007
 
1015
1008
  if collation:
1016
- query = f'SET COLLATION_SERVER={collation}'
1009
+ query = f'SET NAMES {charset} COLLATE {collation}'
1017
1010
  else:
1018
- query = f'SET CHARACTER_SET_SERVER={charset}'
1011
+ query = f'SET NAMES {charset}'
1019
1012
  self._execute_command(COMMAND.COM_QUERY, query)
1020
1013
  self._read_packet()
1021
1014
  self.charset = charset
@@ -1119,6 +1112,19 @@ class Connection(BaseConnection):
1119
1112
  self._get_server_information()
1120
1113
  self._request_authentication()
1121
1114
 
1115
+ # Send "SET NAMES" query on init for:
1116
+ # - Ensure charaset (and collation) is set to the server.
1117
+ # - collation_id in handshake packet may be ignored.
1118
+ # - If collation is not specified, we don't know what is server's
1119
+ # default collation for the charset. For example, default collation
1120
+ # of utf8mb4 is:
1121
+ # - MySQL 5.7, MariaDB 10.x: utf8mb4_general_ci
1122
+ # - MySQL 8.0: utf8mb4_0900_ai_ci
1123
+ #
1124
+ # Reference:
1125
+ # - https://github.com/PyMySQL/PyMySQL/issues/1092
1126
+ # - https://github.com/wagtail/wagtail/issues/9477
1127
+ # - https://zenn.dev/methane/articles/2023-mysql-collation (Japanese)
1122
1128
  self.set_character_set(self.charset, self.collation)
1123
1129
 
1124
1130
  if self.sql_mode is not None:
@@ -8,6 +8,12 @@ from ..utils import results
8
8
  from ..utils.debug import log_query
9
9
  from ..utils.results import get_schema
10
10
 
11
+ try:
12
+ from pydantic import BaseModel
13
+ has_pydantic = True
14
+ except ImportError:
15
+ has_pydantic = False
16
+
11
17
 
12
18
  #: Regular expression for :meth:`Cursor.executemany`.
13
19
  #: executemany only supports simple bulk insert.
@@ -149,6 +155,8 @@ class Cursor(BaseCursor):
149
155
  return tuple(literal(arg) for arg in args)
150
156
  elif dtype is dict or isinstance(args, dict):
151
157
  return {key: literal(val) for (key, val) in args.items()}
158
+ elif has_pydantic and isinstance(args, BaseModel):
159
+ return {key: literal(val) for (key, val) in args.model_dump().items()}
152
160
  # If it's not a dictionary let's try escaping it anyways.
153
161
  # Worst case it will throw a Value error
154
162
  return conn.escape(args)
@@ -6,6 +6,7 @@ import decimal
6
6
  import math
7
7
  import os
8
8
  import unittest
9
+ from typing import Optional
9
10
 
10
11
  from requests.exceptions import InvalidJSONError
11
12
 
@@ -28,6 +29,12 @@ try:
28
29
  except ImportError:
29
30
  has_pygeos = False
30
31
 
32
+ try:
33
+ import pydantic
34
+ has_pydantic = True
35
+ except ImportError:
36
+ has_pydantic = False
37
+
31
38
  import singlestoredb as s2
32
39
  from . import utils
33
40
  # import traceback
@@ -1255,6 +1262,70 @@ class TestBasics(unittest.TestCase):
1255
1262
  except Exception:
1256
1263
  pass
1257
1264
 
1265
+ def test_pydantic(self):
1266
+ if not has_pydantic:
1267
+ self.skipTest('Test requires pydantic')
1268
+
1269
+ tblname = 'foo_' + str(id(self))
1270
+
1271
+ class FooData(pydantic.BaseModel):
1272
+ x: Optional[int]
1273
+ y: Optional[float]
1274
+ z: Optional[str] = None
1275
+
1276
+ self.cur.execute(f'''
1277
+ CREATE TABLE {tblname}(
1278
+ x INT,
1279
+ y DOUBLE,
1280
+ z TEXT
1281
+ )
1282
+ ''')
1283
+
1284
+ self.cur.execute(
1285
+ f'INSERT INTO {tblname}(x, y) VALUES (%(x)s, %(y)s)',
1286
+ FooData(x=2, y=3.23),
1287
+ )
1288
+
1289
+ self.cur.execute('SELECT * FROM ' + tblname)
1290
+
1291
+ assert list(sorted(self.cur.fetchall())) == \
1292
+ list(sorted([(2, 3.23, None)]))
1293
+
1294
+ self.cur.executemany(
1295
+ f'INSERT INTO {tblname}(x) VALUES (%(x)s)',
1296
+ [FooData(x=3, y=3.12), FooData(x=10, y=100.11)],
1297
+ )
1298
+
1299
+ self.cur.execute('SELECT * FROM ' + tblname)
1300
+
1301
+ assert list(sorted(self.cur.fetchall())) == \
1302
+ list(
1303
+ sorted([
1304
+ (2, 3.23, None),
1305
+ (3, None, None),
1306
+ (10, None, None),
1307
+ ]),
1308
+ )
1309
+
1310
+ def test_charset(self):
1311
+ self.skipTest('Skip until charset commands are re-implemented')
1312
+
1313
+ with s2.connect(database=type(self).dbname) as conn:
1314
+ with conn.cursor() as cur:
1315
+ cur.execute('''
1316
+ select json_extract_string('{"foo":"😀"}', "bar");
1317
+ ''')
1318
+
1319
+ if 'http' in self.conn.driver:
1320
+ self.skipTest('Charset is not use in HTTP interface')
1321
+
1322
+ with self.assertRaises(s2.OperationalError):
1323
+ with s2.connect(database=type(self).dbname, charset='utf8') as conn:
1324
+ with conn.cursor() as cur:
1325
+ cur.execute('''
1326
+ select json_extract_string('{"foo":"😀"}', "bar");
1327
+ ''')
1328
+
1258
1329
 
1259
1330
  if __name__ == '__main__':
1260
1331
  import nose2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: singlestoredb
3
- Version: 1.12.2
3
+ Version: 1.12.4
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,6 +1,6 @@
1
- singlestoredb/__init__.py,sha256=Ms1Jw6Pv01LlItPE5vSqQ59kqTYvN3HrSbQSN1i1qv0,1649
1
+ singlestoredb/__init__.py,sha256=k2RhlWhHq2KGERDJKr9bjgAV5KCrLJl2vcEyfWJriGw,1649
2
2
  singlestoredb/auth.py,sha256=u8D9tpKzrqa4ssaHjyZnGDX1q8XBpGtuoOkTkSv7B28,7599
3
- singlestoredb/config.py,sha256=rlF69SiclYyKghNRckX77Ls1ZT23RhSssO1cyYBiHmA,12589
3
+ singlestoredb/config.py,sha256=0XooNOf1dUee26C0io7unfBbdXUyfDIYc_mSpxsVsTI,12592
4
4
  singlestoredb/connection.py,sha256=0HEpjBZXLqQwOTEfveMkgej1H3Kyof47prIHvJJZtoo,45831
5
5
  singlestoredb/converters.py,sha256=Ui-AqdW3pRAQ8A_YcK9EqVYyM4Pt1_Q-tjlotbpK6Cw,20686
6
6
  singlestoredb/exceptions.py,sha256=HuoA6sMRL5qiCiee-_5ddTGmFbYC9Euk8TYUsh5GvTw,3234
@@ -21,10 +21,10 @@ singlestoredb/apps/_uvicorn_util.py,sha256=rEK4nEmq5hbpRgsmK16UVlxe2DyQSq7C5w5WZ
21
21
  singlestoredb/functions/__init__.py,sha256=nPaLVgtb5XDxbRucDFFwjePPh4n40_6jcbxE8HPebkQ,82
22
22
  singlestoredb/functions/decorator.py,sha256=0gfYVCi_GvxKcuDmAcpfIFTTElLD0CBT8RYN_dUrEvU,11485
23
23
  singlestoredb/functions/dtypes.py,sha256=a2vevIug8NhiUCFiSOKwRPpdWU69Gn13ZoQ6Aovskhc,31408
24
- singlestoredb/functions/signature.py,sha256=y3QKjjM9weZjIZyFE-E4SEFx5i3sgP8rqTC0iJ0hhaM,22396
24
+ singlestoredb/functions/signature.py,sha256=qjdCBxtJvNnDWCAruX9kFNuYuGYVCWGEJ-heKRu4MXk,22551
25
25
  singlestoredb/functions/ext/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
26
26
  singlestoredb/functions/ext/arrow.py,sha256=WB7n1ACslyd8nlbFzUvlbxn1BVuEjA9-BGBEqCWlSOo,9061
27
- singlestoredb/functions/ext/asgi.py,sha256=UMZVrw2G_a5SDNy4X7uVJeidfR5HobJN1qLMLqPBAeM,43535
27
+ singlestoredb/functions/ext/asgi.py,sha256=AK7dxe_m8TnFh7IuKKzKO1lCyfJVzs4hRJKW1vTO130,44073
28
28
  singlestoredb/functions/ext/json.py,sha256=XkI8jirxi1T9F-M0p9NpLezph0MRAhYmDiPuU2Id0Uo,10404
29
29
  singlestoredb/functions/ext/mmap.py,sha256=OB6CIYoLe_AYuJM10lE0I6QhZJ5kMhLNbQo2Sp1wiZA,13711
30
30
  singlestoredb/functions/ext/rowdat_1.py,sha256=JgKRsVSQYczFD6cmo2xLilbNPYpyLL2tPOWO1Gh25ow,22306
@@ -43,7 +43,7 @@ singlestoredb/fusion/handlers/stage.py,sha256=kYVjbPys83kf3jX6jWwN8Ju0oEocKVZ3TI
43
43
  singlestoredb/fusion/handlers/utils.py,sha256=ozHOWUraoN8XGTK9JZdhv5HV8AQR8zfUd1yh1kLvUXY,10685
44
44
  singlestoredb/fusion/handlers/workspace.py,sha256=4xN2TFO4yF7KZB2Fcht7IuvoDdAT6fDfDLjixiHZN8w,27506
45
45
  singlestoredb/http/__init__.py,sha256=A_2ZUCCpvRYIA6YDpPy57wL5R1eZ5SfP6I1To5nfJ2s,912
46
- singlestoredb/http/connection.py,sha256=gSqlCxHbjR1745LgUugfDAXU47Uoksu3jVicYI-gO1M,39609
46
+ singlestoredb/http/connection.py,sha256=dNn6OAjCSueR_CIv1T28oDr_TkqXUV05yv1t-XoPrYE,39814
47
47
  singlestoredb/magics/__init__.py,sha256=lZjkT3Webo9c1EQAzlRCRh6B2pckQH8uvNrrB__abcI,1210
48
48
  singlestoredb/magics/run_personal.py,sha256=2f7u1T7iblxGzZurHNgNXLrPBvsvPADZKo_RD_IjYuE,1844
49
49
  singlestoredb/magics/run_shared.py,sha256=SI8dCBRMaGn-xZU7dto4jsAqKBi-Ll14htUsMUSBpJM,1752
@@ -61,9 +61,9 @@ singlestoredb/management/workspace.py,sha256=ze-eE-cO3JCrR3uttVFaBOndDbEE8_qWR2k
61
61
  singlestoredb/mysql/__init__.py,sha256=olUTAvkiERhDW41JXQMawkg-i0tvBEkoTkII1tt6lxU,4492
62
62
  singlestoredb/mysql/_auth.py,sha256=AugRitoUwgRIDFuJxuAH4MWIAmckY7Ji2pP6r_Ng9dY,8043
63
63
  singlestoredb/mysql/charset.py,sha256=-FlONDS_oAUF5B3mIgeHBPb_SCt4zHD33arUeBNctU0,10510
64
- singlestoredb/mysql/connection.py,sha256=TN-_c8JSFSEnpsHNtQ_3DQyOshp-BTx2PlF8g_hDeGQ,73087
64
+ singlestoredb/mysql/connection.py,sha256=6WlhOF-oEoF7V4bl4MTxU6qr6H15-KZvPC-snPmbXVg,73355
65
65
  singlestoredb/mysql/converters.py,sha256=CVe8SDmjbIAhy1xpQ2N5OKWw6t5eWpw-EU3QTlA0Hh0,7500
66
- singlestoredb/mysql/cursors.py,sha256=Eqe7jITRvOo4P_TxIarTumg_2PG1DcCfZ4Uo9IFdDa8,26794
66
+ singlestoredb/mysql/cursors.py,sha256=aOLfHkj83aYZPOVuhJPkZ83CWByszIBRynq0fqSaWvY,27046
67
67
  singlestoredb/mysql/err.py,sha256=-m5rqXi8yhq6b8SCEJ2h0E5Rudh_15dlAU_WbJ1YrM8,2388
68
68
  singlestoredb/mysql/optionfile.py,sha256=DqL-rOQcqQncD5eVbPRkwZqo7Pj3Vh40VLx3E_e87TU,655
69
69
  singlestoredb/mysql/protocol.py,sha256=2GG8qTXy5npqo7z2D2K5T0S8PtoUOS-hFDEXy8VConw,14451
@@ -112,7 +112,7 @@ singlestoredb/tests/test.ipynb,sha256=jrkI2WoSsUA9xQpKTBCHnsDptryQhPdM5QaxfvYRGp
112
112
  singlestoredb/tests/test.sql,sha256=dfMehVCQ9wObSVTQKyQi-fRFDZeqRxV4Cj8doBCPEFM,17679
113
113
  singlestoredb/tests/test2.ipynb,sha256=yd1PE1VK-DwiRd6mYS4_0cPBtuVkvcDtycvTwD-YnDo,218
114
114
  singlestoredb/tests/test2.sql,sha256=D4U2GSlOVeo39U8-RMM4YziJzYFfi4Ztm2YXJVJVAS8,37
115
- singlestoredb/tests/test_basics.py,sha256=1__lEF7FmQF4_pFi5R53TtJidtQznmQ592Ci6aDVgrc,46368
115
+ singlestoredb/tests/test_basics.py,sha256=Dw1irrtf3gWN7tqGruSH6uhWi5zkmCpJl6ZMQxMqlf4,48446
116
116
  singlestoredb/tests/test_config.py,sha256=63lyIQ2KrvGE6C9403B_4Mc90mX4tp42ys5Bih2sXrE,11184
117
117
  singlestoredb/tests/test_connection.py,sha256=fvn-kPdeIMI9RGNz0dNk5ZmTCep1amwWQDHYfPdqO60,119699
118
118
  singlestoredb/tests/test_dbapi.py,sha256=IKq5Hcwx8WikASP8_AB5fo3TXv7ryWPCVGonoly00gI,652
@@ -140,9 +140,9 @@ singlestoredb/utils/results.py,sha256=bJtaUaDiFq26IsPAKZ2FHGB7csMn94EAxLKrP4HaEE
140
140
  singlestoredb/utils/xdict.py,sha256=S9HKgrPrnu_6b7iOwa2KrW8CmU1Uqx0BWdEyogFzWbE,12896
141
141
  sqlx/__init__.py,sha256=aBYiU8DZXCogvWu3yWafOz7bZS5WWwLZXj7oL0dXGyU,85
142
142
  sqlx/magic.py,sha256=JsS9_9aBFaOt91Torm1JPN0c8qB2QmYJmNSKtbSQIY0,3509
143
- singlestoredb-1.12.2.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
144
- singlestoredb-1.12.2.dist-info/METADATA,sha256=sSTAPKm9LSUt3agc3cMgLUEB2IM6Ydp9x9lEojSPAjo,5622
145
- singlestoredb-1.12.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
146
- singlestoredb-1.12.2.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
147
- singlestoredb-1.12.2.dist-info/top_level.txt,sha256=DfFGz7bM4XrshloiCeTABgylT3BUnS8T5pJam3ewT6Q,19
148
- singlestoredb-1.12.2.dist-info/RECORD,,
143
+ singlestoredb-1.12.4.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
144
+ singlestoredb-1.12.4.dist-info/METADATA,sha256=YmOcqvh_bJwEzfcaVM-lG6ane49lQwXy2TJdl0t42Rc,5622
145
+ singlestoredb-1.12.4.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
146
+ singlestoredb-1.12.4.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
147
+ singlestoredb-1.12.4.dist-info/top_level.txt,sha256=DfFGz7bM4XrshloiCeTABgylT3BUnS8T5pJam3ewT6Q,19
148
+ singlestoredb-1.12.4.dist-info/RECORD,,