singlestoredb 1.14.1__py3-none-any.whl → 1.15.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.

Files changed (29) hide show
  1. singlestoredb/__init__.py +14 -10
  2. singlestoredb/apps/_python_udfs.py +3 -3
  3. singlestoredb/config.py +5 -0
  4. singlestoredb/functions/decorator.py +32 -13
  5. singlestoredb/functions/ext/asgi.py +287 -27
  6. singlestoredb/functions/ext/timer.py +98 -0
  7. singlestoredb/functions/typing/numpy.py +20 -0
  8. singlestoredb/functions/typing/pandas.py +2 -0
  9. singlestoredb/functions/typing/polars.py +2 -0
  10. singlestoredb/functions/typing/pyarrow.py +2 -0
  11. singlestoredb/fusion/handler.py +17 -4
  12. singlestoredb/magics/run_personal.py +82 -1
  13. singlestoredb/magics/run_shared.py +82 -1
  14. singlestoredb/management/__init__.py +1 -0
  15. singlestoredb/management/export.py +1 -1
  16. singlestoredb/management/region.py +92 -0
  17. singlestoredb/management/workspace.py +180 -1
  18. singlestoredb/tests/ext_funcs/__init__.py +94 -55
  19. singlestoredb/tests/test.sql +22 -0
  20. singlestoredb/tests/test_ext_func.py +90 -0
  21. singlestoredb/tests/test_fusion.py +4 -1
  22. singlestoredb/tests/test_management.py +253 -20
  23. {singlestoredb-1.14.1.dist-info → singlestoredb-1.15.0.dist-info}/METADATA +3 -2
  24. {singlestoredb-1.14.1.dist-info → singlestoredb-1.15.0.dist-info}/RECORD +29 -24
  25. /singlestoredb/functions/{typing.py → typing/__init__.py} +0 -0
  26. {singlestoredb-1.14.1.dist-info → singlestoredb-1.15.0.dist-info}/LICENSE +0 -0
  27. {singlestoredb-1.14.1.dist-info → singlestoredb-1.15.0.dist-info}/WHEEL +0 -0
  28. {singlestoredb-1.14.1.dist-info → singlestoredb-1.15.0.dist-info}/entry_points.txt +0 -0
  29. {singlestoredb-1.14.1.dist-info → singlestoredb-1.15.0.dist-info}/top_level.txt +0 -0
@@ -1301,17 +1301,24 @@ class StarterWorkspace(object):
1301
1301
  See Also
1302
1302
  --------
1303
1303
  :meth:`WorkspaceManager.get_starter_workspace`
1304
+ :meth:`WorkspaceManager.create_starter_workspace`
1305
+ :meth:`WorkspaceManager.terminate_starter_workspace`
1306
+ :meth:`WorkspaceManager.create_starter_workspace_user`
1304
1307
  :attr:`WorkspaceManager.starter_workspaces`
1305
1308
 
1306
1309
  """
1307
1310
 
1308
1311
  name: str
1309
1312
  id: str
1313
+ database_name: str
1314
+ endpoint: Optional[str]
1310
1315
 
1311
1316
  def __init__(
1312
1317
  self,
1313
1318
  name: str,
1314
1319
  id: str,
1320
+ database_name: str,
1321
+ endpoint: Optional[str] = None,
1315
1322
  ):
1316
1323
  #: Name of the starter workspace
1317
1324
  self.name = name
@@ -1319,6 +1326,13 @@ class StarterWorkspace(object):
1319
1326
  #: Unique ID of the starter workspace
1320
1327
  self.id = id
1321
1328
 
1329
+ #: Name of the database associated with the starter workspace
1330
+ self.database_name = database_name
1331
+
1332
+ #: Endpoint to connect to the starter workspace. The endpoint is in the form
1333
+ #: of ``hostname:port``
1334
+ self.endpoint = endpoint
1335
+
1322
1336
  self._manager: Optional[WorkspaceManager] = None
1323
1337
 
1324
1338
  def __str__(self) -> str:
@@ -1351,10 +1365,63 @@ class StarterWorkspace(object):
1351
1365
  out = cls(
1352
1366
  name=obj['name'],
1353
1367
  id=obj['virtualWorkspaceID'],
1368
+ database_name=obj['databaseName'],
1369
+ endpoint=obj.get('endpoint'),
1354
1370
  )
1355
1371
  out._manager = manager
1356
1372
  return out
1357
1373
 
1374
+ def connect(self, **kwargs: Any) -> connection.Connection:
1375
+ """
1376
+ Create a connection to the database server for this starter workspace.
1377
+
1378
+ Parameters
1379
+ ----------
1380
+ **kwargs : keyword-arguments, optional
1381
+ Parameters to the SingleStoreDB `connect` function except host
1382
+ and port which are supplied by the starter workspace object
1383
+
1384
+ Returns
1385
+ -------
1386
+ :class:`Connection`
1387
+
1388
+ """
1389
+ if not self.endpoint:
1390
+ raise ManagementError(
1391
+ msg='An endpoint has not been set in this '
1392
+ 'starter workspace configuration',
1393
+ )
1394
+ # Parse endpoint as host:port
1395
+ if ':' in self.endpoint:
1396
+ host, port = self.endpoint.split(':', 1)
1397
+ kwargs['host'] = host
1398
+ kwargs['port'] = int(port)
1399
+ else:
1400
+ kwargs['host'] = self.endpoint
1401
+ return connection.connect(**kwargs)
1402
+
1403
+ def terminate(self) -> None:
1404
+ """Terminate the starter workspace."""
1405
+ if self._manager is None:
1406
+ raise ManagementError(
1407
+ msg='No workspace manager is associated with this object.',
1408
+ )
1409
+ self._manager._delete(f'sharedtier/virtualWorkspaces/{self.id}')
1410
+
1411
+ def refresh(self) -> StarterWorkspace:
1412
+ """Update the object to the current state."""
1413
+ if self._manager is None:
1414
+ raise ManagementError(
1415
+ msg='No workspace manager is associated with this object.',
1416
+ )
1417
+ new_obj = self._manager.get_starter_workspace(self.id)
1418
+ for name, value in vars(new_obj).items():
1419
+ if isinstance(value, Mapping):
1420
+ setattr(self, name, snake_to_camel_dict(value))
1421
+ else:
1422
+ setattr(self, name, value)
1423
+ return self
1424
+
1358
1425
  @property
1359
1426
  def organization(self) -> Organization:
1360
1427
  if self._manager is None:
@@ -1375,7 +1442,7 @@ class StarterWorkspace(object):
1375
1442
  stages = stage
1376
1443
 
1377
1444
  @property
1378
- def starter_workspaces(self) -> NamedList[StarterWorkspace]:
1445
+ def starter_workspaces(self) -> NamedList['StarterWorkspace']:
1379
1446
  """Return a list of available starter workspaces."""
1380
1447
  if self._manager is None:
1381
1448
  raise ManagementError(
@@ -1386,6 +1453,64 @@ class StarterWorkspace(object):
1386
1453
  [StarterWorkspace.from_dict(item, self._manager) for item in res.json()],
1387
1454
  )
1388
1455
 
1456
+ def create_user(
1457
+ self,
1458
+ user_name: str,
1459
+ password: Optional[str] = None,
1460
+ ) -> Dict[str, str]:
1461
+ """
1462
+ Create a new user for this starter workspace.
1463
+
1464
+ Parameters
1465
+ ----------
1466
+ user_name : str
1467
+ The starter workspace user name to connect the new user to the database
1468
+ password : str, optional
1469
+ Password for the new user. If not provided, a password will be
1470
+ auto-generated by the system.
1471
+
1472
+ Returns
1473
+ -------
1474
+ Dict[str, str]
1475
+ Dictionary containing 'userID' and 'password' of the created user
1476
+
1477
+ Raises
1478
+ ------
1479
+ ManagementError
1480
+ If no workspace manager is associated with this object.
1481
+ """
1482
+ if self._manager is None:
1483
+ raise ManagementError(
1484
+ msg='No workspace manager is associated with this object.',
1485
+ )
1486
+
1487
+ payload = {
1488
+ 'userName': user_name,
1489
+ }
1490
+ if password is not None:
1491
+ payload['password'] = password
1492
+
1493
+ res = self._manager._post(
1494
+ f'sharedtier/virtualWorkspaces/{self.id}/users',
1495
+ json=payload,
1496
+ )
1497
+
1498
+ response_data = res.json()
1499
+ user_id = response_data.get('userID')
1500
+ if not user_id:
1501
+ raise ManagementError(msg='No userID returned from API')
1502
+
1503
+ # Return the password provided by user or generated by API
1504
+ returned_password = password if password is not None \
1505
+ else response_data.get('password')
1506
+ if not returned_password:
1507
+ raise ManagementError(msg='No password available from API response')
1508
+
1509
+ return {
1510
+ 'user_id': user_id,
1511
+ 'password': returned_password,
1512
+ }
1513
+
1389
1514
 
1390
1515
  class Billing(object):
1391
1516
  """Billing information."""
@@ -1522,6 +1647,14 @@ class WorkspaceManager(Manager):
1522
1647
  res = self._get('regions')
1523
1648
  return NamedList([Region.from_dict(item, self) for item in res.json()])
1524
1649
 
1650
+ @ttl_property(datetime.timedelta(hours=1))
1651
+ def shared_tier_regions(self) -> NamedList[Region]:
1652
+ """Return a list of regions that support shared tier workspaces."""
1653
+ res = self._get('regions/sharedtier')
1654
+ return NamedList(
1655
+ [Region.from_dict(item, self) for item in res.json()],
1656
+ )
1657
+
1525
1658
  def create_workspace_group(
1526
1659
  self,
1527
1660
  name: str,
@@ -1717,6 +1850,52 @@ class WorkspaceManager(Manager):
1717
1850
  res = self._get(f'sharedtier/virtualWorkspaces/{id}')
1718
1851
  return StarterWorkspace.from_dict(res.json(), manager=self)
1719
1852
 
1853
+ def create_starter_workspace(
1854
+ self,
1855
+ name: str,
1856
+ database_name: str,
1857
+ workspace_group: dict[str, str],
1858
+ ) -> 'StarterWorkspace':
1859
+ """
1860
+ Create a new starter (shared tier) workspace.
1861
+
1862
+ Parameters
1863
+ ----------
1864
+ name : str
1865
+ Name of the starter workspace
1866
+ database_name : str
1867
+ Name of the database for the starter workspace
1868
+ workspace_group : dict[str, str]
1869
+ Workspace group input (dict with keys: 'cell_id')
1870
+
1871
+ Returns
1872
+ -------
1873
+ :class:`StarterWorkspace`
1874
+ """
1875
+ if not workspace_group or not isinstance(workspace_group, dict):
1876
+ raise ValueError(
1877
+ 'workspace_group must be a dict with keys: '
1878
+ "'cell_id'",
1879
+ )
1880
+ if set(workspace_group.keys()) != {'cell_id'}:
1881
+ raise ValueError("workspace_group must contain only 'cell_id'")
1882
+
1883
+ payload = {
1884
+ 'name': name,
1885
+ 'databaseName': database_name,
1886
+ 'workspaceGroup': {
1887
+ 'cellID': workspace_group['cell_id'],
1888
+ },
1889
+ }
1890
+
1891
+ res = self._post('sharedtier/virtualWorkspaces', json=payload)
1892
+ virtual_workspace_id = res.json().get('virtualWorkspaceID')
1893
+ if not virtual_workspace_id:
1894
+ raise ManagementError(msg='No virtualWorkspaceID returned from API')
1895
+
1896
+ res = self._get(f'sharedtier/virtualWorkspaces/{virtual_workspace_id}')
1897
+ return StarterWorkspace.from_dict(res.json(), self)
1898
+
1720
1899
 
1721
1900
  def manage_workspaces(
1722
1901
  access_token: Optional[str] = None,
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  # mypy: disable-error-code="type-arg"
3
+ import asyncio
4
+ import time
3
5
  import typing
4
6
  from typing import List
5
7
  from typing import NamedTuple
@@ -7,10 +9,6 @@ from typing import Optional
7
9
  from typing import Tuple
8
10
 
9
11
  import numpy as np
10
- import numpy.typing as npt
11
- import pandas as pd
12
- import polars as pl
13
- import pyarrow as pa
14
12
 
15
13
  import singlestoredb.functions.dtypes as dt
16
14
  from singlestoredb.functions import Masked
@@ -24,6 +22,10 @@ from singlestoredb.functions.dtypes import MEDIUMINT
24
22
  from singlestoredb.functions.dtypes import SMALLINT
25
23
  from singlestoredb.functions.dtypes import TEXT
26
24
  from singlestoredb.functions.dtypes import TINYINT
25
+ from singlestoredb.functions.typing import numpy as npt
26
+ from singlestoredb.functions.typing import pandas as pdt
27
+ from singlestoredb.functions.typing import polars as plt
28
+ from singlestoredb.functions.typing import pyarrow as pat
27
29
 
28
30
 
29
31
  @udf
@@ -36,19 +38,44 @@ def double_mult(x: float, y: float) -> float:
36
38
  return x * y
37
39
 
38
40
 
41
+ @udf(timeout=2)
42
+ def timeout_double_mult(x: float, y: float) -> float:
43
+ time.sleep(5)
44
+ return x * y
45
+
46
+
47
+ @udf
48
+ async def async_double_mult(x: float, y: float) -> float:
49
+ return x * y
50
+
51
+
52
+ @udf(timeout=2)
53
+ async def async_timeout_double_mult(x: float, y: float) -> float:
54
+ await asyncio.sleep(5)
55
+ return x * y
56
+
57
+
39
58
  @udf(
40
59
  args=[DOUBLE(nullable=False), DOUBLE(nullable=False)],
41
60
  returns=DOUBLE(nullable=False),
42
61
  )
43
- def pandas_double_mult(x: pd.Series, y: pd.Series) -> pd.Series:
62
+ def pandas_double_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
44
63
  return x * y
45
64
 
46
65
 
47
66
  @udf
48
67
  def numpy_double_mult(
49
- x: npt.NDArray[np.float64],
50
- y: npt.NDArray[np.float64],
51
- ) -> npt.NDArray[np.float64]:
68
+ x: npt.Float64Array,
69
+ y: npt.Float64Array,
70
+ ) -> npt.Float64Array:
71
+ return x * y
72
+
73
+
74
+ @udf
75
+ async def async_numpy_double_mult(
76
+ x: npt.Float64Array,
77
+ y: npt.Float64Array,
78
+ ) -> npt.Float64Array:
52
79
  return x * y
53
80
 
54
81
 
@@ -56,7 +83,7 @@ def numpy_double_mult(
56
83
  args=[DOUBLE(nullable=False), DOUBLE(nullable=False)],
57
84
  returns=DOUBLE(nullable=False),
58
85
  )
59
- def arrow_double_mult(x: pa.Array, y: pa.Array) -> pa.Array:
86
+ def arrow_double_mult(x: pat.Array, y: pat.Array) -> pat.Array:
60
87
  import pyarrow.compute as pc
61
88
  return pc.multiply(x, y)
62
89
 
@@ -65,7 +92,7 @@ def arrow_double_mult(x: pa.Array, y: pa.Array) -> pa.Array:
65
92
  args=[DOUBLE(nullable=False), DOUBLE(nullable=False)],
66
93
  returns=DOUBLE(nullable=False),
67
94
  )
68
- def polars_double_mult(x: pl.Series, y: pl.Series) -> pl.Series:
95
+ def polars_double_mult(x: plt.Series, y: plt.Series) -> plt.Series:
69
96
  return x * y
70
97
 
71
98
 
@@ -106,12 +133,12 @@ def tinyint_mult(x: Optional[int], y: Optional[int]) -> Optional[int]:
106
133
 
107
134
 
108
135
  @tinyint_udf
109
- def pandas_tinyint_mult(x: pd.Series, y: pd.Series) -> pd.Series:
136
+ def pandas_tinyint_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
110
137
  return x * y
111
138
 
112
139
 
113
140
  @tinyint_udf
114
- def polars_tinyint_mult(x: pl.Series, y: pl.Series) -> pl.Series:
141
+ def polars_tinyint_mult(x: plt.Series, y: plt.Series) -> plt.Series:
115
142
  return x * y
116
143
 
117
144
 
@@ -121,7 +148,7 @@ def numpy_tinyint_mult(x: np.ndarray, y: np.ndarray) -> np.ndarray:
121
148
 
122
149
 
123
150
  @tinyint_udf
124
- def arrow_tinyint_mult(x: pa.Array, y: pa.Array) -> pa.Array:
151
+ def arrow_tinyint_mult(x: pat.Array, y: pat.Array) -> pat.Array:
125
152
  import pyarrow.compute as pc
126
153
  return pc.multiply(x, y)
127
154
 
@@ -144,12 +171,12 @@ def smallint_mult(x: Optional[int], y: Optional[int]) -> Optional[int]:
144
171
 
145
172
 
146
173
  @smallint_udf
147
- def pandas_smallint_mult(x: pd.Series, y: pd.Series) -> pd.Series:
174
+ def pandas_smallint_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
148
175
  return x * y
149
176
 
150
177
 
151
178
  @smallint_udf
152
- def polars_smallint_mult(x: pl.Series, y: pl.Series) -> pl.Series:
179
+ def polars_smallint_mult(x: plt.Series, y: plt.Series) -> plt.Series:
153
180
  return x * y
154
181
 
155
182
 
@@ -159,7 +186,7 @@ def numpy_smallint_mult(x: np.ndarray, y: np.ndarray) -> np.ndarray:
159
186
 
160
187
 
161
188
  @smallint_udf
162
- def arrow_smallint_mult(x: pa.Array, y: pa.Array) -> pa.Array:
189
+ def arrow_smallint_mult(x: pat.Array, y: pat.Array) -> pat.Array:
163
190
  import pyarrow.compute as pc
164
191
  return pc.multiply(x, y)
165
192
 
@@ -183,12 +210,12 @@ def mediumint_mult(x: Optional[int], y: Optional[int]) -> Optional[int]:
183
210
 
184
211
 
185
212
  @mediumint_udf
186
- def pandas_mediumint_mult(x: pd.Series, y: pd.Series) -> pd.Series:
213
+ def pandas_mediumint_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
187
214
  return x * y
188
215
 
189
216
 
190
217
  @mediumint_udf
191
- def polars_mediumint_mult(x: pl.Series, y: pl.Series) -> pl.Series:
218
+ def polars_mediumint_mult(x: plt.Series, y: plt.Series) -> plt.Series:
192
219
  return x * y
193
220
 
194
221
 
@@ -198,7 +225,7 @@ def numpy_mediumint_mult(x: np.ndarray, y: np.ndarray) -> np.ndarray:
198
225
 
199
226
 
200
227
  @mediumint_udf
201
- def arrow_mediumint_mult(x: pa.Array, y: pa.Array) -> pa.Array:
228
+ def arrow_mediumint_mult(x: pat.Array, y: pat.Array) -> pat.Array:
202
229
  import pyarrow.compute as pc
203
230
  return pc.multiply(x, y)
204
231
 
@@ -222,12 +249,12 @@ def bigint_mult(x: Optional[int], y: Optional[int]) -> Optional[int]:
222
249
 
223
250
 
224
251
  @bigint_udf
225
- def pandas_bigint_mult(x: pd.Series, y: pd.Series) -> pd.Series:
252
+ def pandas_bigint_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
226
253
  return x * y
227
254
 
228
255
 
229
256
  @bigint_udf
230
- def polars_bigint_mult(x: pl.Series, y: pl.Series) -> pl.Series:
257
+ def polars_bigint_mult(x: plt.Series, y: plt.Series) -> plt.Series:
231
258
  return x * y
232
259
 
233
260
 
@@ -237,7 +264,7 @@ def numpy_bigint_mult(x: np.ndarray, y: np.ndarray) -> np.ndarray:
237
264
 
238
265
 
239
266
  @bigint_udf
240
- def arrow_bigint_mult(x: pa.Array, y: pa.Array) -> pa.Array:
267
+ def arrow_bigint_mult(x: pat.Array, y: pat.Array) -> pat.Array:
241
268
  import pyarrow.compute as pc
242
269
  return pc.multiply(x, y)
243
270
 
@@ -261,12 +288,12 @@ def nullable_tinyint_mult(x: Optional[int], y: Optional[int]) -> Optional[int]:
261
288
 
262
289
 
263
290
  @nullable_tinyint_udf
264
- def pandas_nullable_tinyint_mult(x: pd.Series, y: pd.Series) -> pd.Series:
291
+ def pandas_nullable_tinyint_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
265
292
  return x * y
266
293
 
267
294
 
268
295
  @nullable_tinyint_udf
269
- def polars_nullable_tinyint_mult(x: pl.Series, y: pl.Series) -> pl.Series:
296
+ def polars_nullable_tinyint_mult(x: plt.Series, y: plt.Series) -> plt.Series:
270
297
  return x * y
271
298
 
272
299
 
@@ -276,7 +303,7 @@ def numpy_nullable_tinyint_mult(x: np.ndarray, y: np.ndarray) -> np.ndarray:
276
303
 
277
304
 
278
305
  @nullable_tinyint_udf
279
- def arrow_nullable_tinyint_mult(x: pa.Array, y: pa.Array) -> pa.Array:
306
+ def arrow_nullable_tinyint_mult(x: pat.Array, y: pat.Array) -> pat.Array:
280
307
  import pyarrow.compute as pc
281
308
  return pc.multiply(x, y)
282
309
 
@@ -299,12 +326,12 @@ def nullable_smallint_mult(x: Optional[int], y: Optional[int]) -> Optional[int]:
299
326
 
300
327
 
301
328
  @nullable_smallint_udf
302
- def pandas_nullable_smallint_mult(x: pd.Series, y: pd.Series) -> pd.Series:
329
+ def pandas_nullable_smallint_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
303
330
  return x * y
304
331
 
305
332
 
306
333
  @nullable_smallint_udf
307
- def polars_nullable_smallint_mult(x: pl.Series, y: pl.Series) -> pl.Series:
334
+ def polars_nullable_smallint_mult(x: plt.Series, y: plt.Series) -> plt.Series:
308
335
  return x * y
309
336
 
310
337
 
@@ -314,7 +341,7 @@ def numpy_nullable_smallint_mult(x: np.ndarray, y: np.ndarray) -> np.ndarray:
314
341
 
315
342
 
316
343
  @nullable_smallint_udf
317
- def arrow_nullable_smallint_mult(x: pa.Array, y: pa.Array) -> pa.Array:
344
+ def arrow_nullable_smallint_mult(x: pat.Array, y: pat.Array) -> pat.Array:
318
345
  import pyarrow.compute as pc
319
346
  return pc.multiply(x, y)
320
347
 
@@ -338,12 +365,12 @@ def nullable_mediumint_mult(x: Optional[int], y: Optional[int]) -> Optional[int]
338
365
 
339
366
 
340
367
  @nullable_mediumint_udf
341
- def pandas_nullable_mediumint_mult(x: pd.Series, y: pd.Series) -> pd.Series:
368
+ def pandas_nullable_mediumint_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
342
369
  return x * y
343
370
 
344
371
 
345
372
  @nullable_mediumint_udf
346
- def polars_nullable_mediumint_mult(x: pl.Series, y: pl.Series) -> pl.Series:
373
+ def polars_nullable_mediumint_mult(x: plt.Series, y: plt.Series) -> plt.Series:
347
374
  return x * y
348
375
 
349
376
 
@@ -353,7 +380,7 @@ def numpy_nullable_mediumint_mult(x: np.ndarray, y: np.ndarray) -> np.ndarray:
353
380
 
354
381
 
355
382
  @nullable_mediumint_udf
356
- def arrow_nullable_mediumint_mult(x: pa.Array, y: pa.Array) -> pa.Array:
383
+ def arrow_nullable_mediumint_mult(x: pat.Array, y: pat.Array) -> pat.Array:
357
384
  import pyarrow.compute as pc
358
385
  return pc.multiply(x, y)
359
386
 
@@ -377,12 +404,12 @@ def nullable_bigint_mult(x: Optional[int], y: Optional[int]) -> Optional[int]:
377
404
 
378
405
 
379
406
  @nullable_bigint_udf
380
- def pandas_nullable_bigint_mult(x: pd.Series, y: pd.Series) -> pd.Series:
407
+ def pandas_nullable_bigint_mult(x: pdt.Series, y: pdt.Series) -> pdt.Series:
381
408
  return x * y
382
409
 
383
410
 
384
411
  @nullable_bigint_udf
385
- def polars_nullable_bigint_mult(x: pl.Series, y: pl.Series) -> pl.Series:
412
+ def polars_nullable_bigint_mult(x: plt.Series, y: plt.Series) -> plt.Series:
386
413
  return x * y
387
414
 
388
415
 
@@ -392,7 +419,7 @@ def numpy_nullable_bigint_mult(x: np.ndarray, y: np.ndarray) -> np.ndarray:
392
419
 
393
420
 
394
421
  @nullable_bigint_udf
395
- def arrow_nullable_bigint_mult(x: pa.Array, y: pa.Array) -> pa.Array:
422
+ def arrow_nullable_bigint_mult(x: pat.Array, y: pat.Array) -> pat.Array:
396
423
  import pyarrow.compute as pc
397
424
  return pc.multiply(x, y)
398
425
 
@@ -410,7 +437,7 @@ def string_mult(x: str, times: int) -> str:
410
437
 
411
438
 
412
439
  @udf(args=[TEXT(nullable=False), BIGINT(nullable=False)], returns=TEXT(nullable=False))
413
- def pandas_string_mult(x: pd.Series, times: pd.Series) -> pd.Series:
440
+ def pandas_string_mult(x: pdt.Series, times: pdt.Series) -> pdt.Series:
414
441
  return x * times
415
442
 
416
443
 
@@ -447,8 +474,8 @@ def nullable_string_mult(x: Optional[str], times: Optional[int]) -> Optional[str
447
474
  returns=TINYINT(nullable=True),
448
475
  )
449
476
  def pandas_nullable_tinyint_mult_with_masks(
450
- x: Masked[pd.Series], y: Masked[pd.Series],
451
- ) -> Masked[pd.Series]:
477
+ x: Masked[pdt.Series], y: Masked[pdt.Series],
478
+ ) -> Masked[pdt.Series]:
452
479
  x_data, x_nulls = x
453
480
  y_data, y_nulls = y
454
481
  return Masked(x_data * y_data, x_nulls | y_nulls)
@@ -468,8 +495,8 @@ def numpy_nullable_tinyint_mult_with_masks(
468
495
  returns=TINYINT(nullable=True),
469
496
  )
470
497
  def polars_nullable_tinyint_mult_with_masks(
471
- x: Masked[pl.Series], y: Masked[pl.Series],
472
- ) -> Masked[pl.Series]:
498
+ x: Masked[plt.Series], y: Masked[plt.Series],
499
+ ) -> Masked[plt.Series]:
473
500
  x_data, x_nulls = x
474
501
  y_data, y_nulls = y
475
502
  return Masked(x_data * y_data, x_nulls | y_nulls)
@@ -480,8 +507,8 @@ def polars_nullable_tinyint_mult_with_masks(
480
507
  returns=TINYINT(nullable=True),
481
508
  )
482
509
  def arrow_nullable_tinyint_mult_with_masks(
483
- x: Masked[pa.Array], y: Masked[pa.Array],
484
- ) -> Masked[pa.Array]:
510
+ x: Masked[pat.Array], y: Masked[pat.Array],
511
+ ) -> Masked[pat.Array]:
485
512
  import pyarrow.compute as pc
486
513
  x_data, x_nulls = x
487
514
  y_data, y_nulls = y
@@ -489,7 +516,7 @@ def arrow_nullable_tinyint_mult_with_masks(
489
516
 
490
517
 
491
518
  @udf(returns=[TEXT(nullable=False, name='res')])
492
- def numpy_fixed_strings() -> Table[npt.NDArray[np.str_]]:
519
+ def numpy_fixed_strings() -> Table[npt.StrArray]:
493
520
  out = np.array(
494
521
  [
495
522
  'hello',
@@ -502,7 +529,7 @@ def numpy_fixed_strings() -> Table[npt.NDArray[np.str_]]:
502
529
 
503
530
 
504
531
  @udf(returns=[TEXT(nullable=False, name='res'), TINYINT(nullable=False, name='res2')])
505
- def numpy_fixed_strings_2() -> Table[npt.NDArray[np.str_], npt.NDArray[np.int8]]:
532
+ def numpy_fixed_strings_2() -> Table[npt.StrArray, npt.Int8Array]:
506
533
  out = np.array(
507
534
  [
508
535
  'hello',
@@ -515,7 +542,7 @@ def numpy_fixed_strings_2() -> Table[npt.NDArray[np.str_], npt.NDArray[np.int8]]
515
542
 
516
543
 
517
544
  @udf(returns=[BLOB(nullable=False, name='res')])
518
- def numpy_fixed_binary() -> Table[npt.NDArray[np.bytes_]]:
545
+ def numpy_fixed_binary() -> Table[npt.BytesArray]:
519
546
  out = np.array(
520
547
  [
521
548
  'hello'.encode('utf8'),
@@ -537,6 +564,11 @@ def table_function(n: int) -> Table[List[int]]:
537
564
  return Table([10] * n)
538
565
 
539
566
 
567
+ @udf
568
+ async def async_table_function(n: int) -> Table[List[int]]:
569
+ return Table([10] * n)
570
+
571
+
540
572
  @udf(
541
573
  returns=[
542
574
  dt.INT(name='c_int', nullable=False),
@@ -561,8 +593,8 @@ def table_function_struct(n: int) -> Table[List[MyTable]]:
561
593
 
562
594
  @udf
563
595
  def vec_function(
564
- x: npt.NDArray[np.float64], y: npt.NDArray[np.float64],
565
- ) -> npt.NDArray[np.float64]:
596
+ x: npt.Float64Array, y: npt.Float64Array,
597
+ ) -> npt.Float64Array:
566
598
  return x * y
567
599
 
568
600
 
@@ -577,8 +609,8 @@ class VecOutputs(typing.NamedTuple):
577
609
 
578
610
  @udf(args=VecInputs, returns=VecOutputs)
579
611
  def vec_function_ints(
580
- x: npt.NDArray[np.int_], y: npt.NDArray[np.int_],
581
- ) -> npt.NDArray[np.int_]:
612
+ x: npt.IntArray, y: npt.IntArray,
613
+ ) -> npt.IntArray:
582
614
  return x * y
583
615
 
584
616
 
@@ -589,9 +621,16 @@ class DFOutputs(typing.NamedTuple):
589
621
 
590
622
  @udf(args=VecInputs, returns=DFOutputs)
591
623
  def vec_function_df(
592
- x: npt.NDArray[np.int_], y: npt.NDArray[np.int_],
593
- ) -> Table[pd.DataFrame]:
594
- return pd.DataFrame(dict(res=[1, 2, 3], res2=[1.1, 2.2, 3.3]))
624
+ x: npt.IntArray, y: npt.IntArray,
625
+ ) -> Table[pdt.DataFrame]:
626
+ return pdt.DataFrame(dict(res=[1, 2, 3], res2=[1.1, 2.2, 3.3]))
627
+
628
+
629
+ @udf(args=VecInputs, returns=DFOutputs)
630
+ async def async_vec_function_df(
631
+ x: npt.IntArray, y: npt.IntArray,
632
+ ) -> Table[pdt.DataFrame]:
633
+ return pdt.DataFrame(dict(res=[1, 2, 3], res2=[1.1, 2.2, 3.3]))
595
634
 
596
635
 
597
636
  class MaskOutputs(typing.NamedTuple):
@@ -600,8 +639,8 @@ class MaskOutputs(typing.NamedTuple):
600
639
 
601
640
  @udf(args=VecInputs, returns=MaskOutputs)
602
641
  def vec_function_ints_masked(
603
- x: Masked[npt.NDArray[np.int_]], y: Masked[npt.NDArray[np.int_]],
604
- ) -> Table[Masked[npt.NDArray[np.int_]]]:
642
+ x: Masked[npt.IntArray], y: Masked[npt.IntArray],
643
+ ) -> Table[Masked[npt.IntArray]]:
605
644
  x_data, x_nulls = x
606
645
  y_data, y_nulls = y
607
646
  return Table(Masked(x_data * y_data, x_nulls | y_nulls))
@@ -614,8 +653,8 @@ class MaskOutputs2(typing.NamedTuple):
614
653
 
615
654
  @udf(args=VecInputs, returns=MaskOutputs2)
616
655
  def vec_function_ints_masked2(
617
- x: Masked[npt.NDArray[np.int_]], y: Masked[npt.NDArray[np.int_]],
618
- ) -> Table[Masked[npt.NDArray[np.int_]], Masked[npt.NDArray[np.int_]]]:
656
+ x: Masked[npt.IntArray], y: Masked[npt.IntArray],
657
+ ) -> Table[Masked[npt.IntArray], Masked[npt.IntArray]]:
619
658
  x_data, x_nulls = x
620
659
  y_data, y_nulls = y
621
660
  return Table(
@@ -14,6 +14,28 @@ INSERT INTO data SET id='e', name='elephants', value=0;
14
14
 
15
15
  COMMIT;
16
16
 
17
+ CREATE ROWSTORE TABLE IF NOT EXISTS longer_data (
18
+ id VARCHAR(255) NOT NULL,
19
+ name VARCHAR(255) NOT NULL,
20
+ value BIGINT NOT NULL,
21
+ PRIMARY KEY (id) USING HASH
22
+ ) DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci;
23
+
24
+ INSERT INTO longer_data SET id='a', name='antelopes', value=2;
25
+ INSERT INTO longer_data SET id='b', name='bears', value=2;
26
+ INSERT INTO longer_data SET id='c', name='cats', value=5;
27
+ INSERT INTO longer_data SET id='d', name='dogs', value=4;
28
+ INSERT INTO longer_data SET id='e', name='elephants', value=0;
29
+ INSERT INTO longer_data SET id='f', name='ferrets', value=2;
30
+ INSERT INTO longer_data SET id='g', name='gorillas', value=4;
31
+ INSERT INTO longer_data SET id='h', name='horses', value=6;
32
+ INSERT INTO longer_data SET id='i', name='iguanas', value=2;
33
+ INSERT INTO longer_data SET id='j', name='jaguars', value=0;
34
+ INSERT INTO longer_data SET id='k', name='kiwis', value=0;
35
+ INSERT INTO longer_data SET id='l', name='leopards', value=1;
36
+
37
+ COMMIT;
38
+
17
39
  CREATE ROWSTORE TABLE IF NOT EXISTS data_with_nulls (
18
40
  id VARCHAR(255) NOT NULL,
19
41
  name VARCHAR(255),