singlestoredb 1.16.1__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.
Files changed (183) hide show
  1. singlestoredb/__init__.py +75 -0
  2. singlestoredb/ai/__init__.py +2 -0
  3. singlestoredb/ai/chat.py +139 -0
  4. singlestoredb/ai/embeddings.py +128 -0
  5. singlestoredb/alchemy/__init__.py +90 -0
  6. singlestoredb/apps/__init__.py +3 -0
  7. singlestoredb/apps/_cloud_functions.py +90 -0
  8. singlestoredb/apps/_config.py +72 -0
  9. singlestoredb/apps/_connection_info.py +18 -0
  10. singlestoredb/apps/_dashboards.py +47 -0
  11. singlestoredb/apps/_process.py +32 -0
  12. singlestoredb/apps/_python_udfs.py +100 -0
  13. singlestoredb/apps/_stdout_supress.py +30 -0
  14. singlestoredb/apps/_uvicorn_util.py +36 -0
  15. singlestoredb/auth.py +245 -0
  16. singlestoredb/config.py +484 -0
  17. singlestoredb/connection.py +1487 -0
  18. singlestoredb/converters.py +950 -0
  19. singlestoredb/docstring/__init__.py +33 -0
  20. singlestoredb/docstring/attrdoc.py +126 -0
  21. singlestoredb/docstring/common.py +230 -0
  22. singlestoredb/docstring/epydoc.py +267 -0
  23. singlestoredb/docstring/google.py +412 -0
  24. singlestoredb/docstring/numpydoc.py +562 -0
  25. singlestoredb/docstring/parser.py +100 -0
  26. singlestoredb/docstring/py.typed +1 -0
  27. singlestoredb/docstring/rest.py +256 -0
  28. singlestoredb/docstring/tests/__init__.py +1 -0
  29. singlestoredb/docstring/tests/_pydoctor.py +21 -0
  30. singlestoredb/docstring/tests/test_epydoc.py +729 -0
  31. singlestoredb/docstring/tests/test_google.py +1007 -0
  32. singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
  33. singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
  34. singlestoredb/docstring/tests/test_parser.py +248 -0
  35. singlestoredb/docstring/tests/test_rest.py +547 -0
  36. singlestoredb/docstring/tests/test_util.py +70 -0
  37. singlestoredb/docstring/util.py +141 -0
  38. singlestoredb/exceptions.py +120 -0
  39. singlestoredb/functions/__init__.py +16 -0
  40. singlestoredb/functions/decorator.py +201 -0
  41. singlestoredb/functions/dtypes.py +1793 -0
  42. singlestoredb/functions/ext/__init__.py +1 -0
  43. singlestoredb/functions/ext/arrow.py +375 -0
  44. singlestoredb/functions/ext/asgi.py +2133 -0
  45. singlestoredb/functions/ext/json.py +420 -0
  46. singlestoredb/functions/ext/mmap.py +413 -0
  47. singlestoredb/functions/ext/rowdat_1.py +724 -0
  48. singlestoredb/functions/ext/timer.py +89 -0
  49. singlestoredb/functions/ext/utils.py +218 -0
  50. singlestoredb/functions/signature.py +1578 -0
  51. singlestoredb/functions/typing/__init__.py +41 -0
  52. singlestoredb/functions/typing/numpy.py +20 -0
  53. singlestoredb/functions/typing/pandas.py +2 -0
  54. singlestoredb/functions/typing/polars.py +2 -0
  55. singlestoredb/functions/typing/pyarrow.py +2 -0
  56. singlestoredb/functions/utils.py +421 -0
  57. singlestoredb/fusion/__init__.py +11 -0
  58. singlestoredb/fusion/graphql.py +213 -0
  59. singlestoredb/fusion/handler.py +916 -0
  60. singlestoredb/fusion/handlers/__init__.py +0 -0
  61. singlestoredb/fusion/handlers/export.py +525 -0
  62. singlestoredb/fusion/handlers/files.py +690 -0
  63. singlestoredb/fusion/handlers/job.py +660 -0
  64. singlestoredb/fusion/handlers/models.py +250 -0
  65. singlestoredb/fusion/handlers/stage.py +502 -0
  66. singlestoredb/fusion/handlers/utils.py +324 -0
  67. singlestoredb/fusion/handlers/workspace.py +956 -0
  68. singlestoredb/fusion/registry.py +249 -0
  69. singlestoredb/fusion/result.py +399 -0
  70. singlestoredb/http/__init__.py +27 -0
  71. singlestoredb/http/connection.py +1267 -0
  72. singlestoredb/magics/__init__.py +34 -0
  73. singlestoredb/magics/run_personal.py +137 -0
  74. singlestoredb/magics/run_shared.py +134 -0
  75. singlestoredb/management/__init__.py +9 -0
  76. singlestoredb/management/billing_usage.py +148 -0
  77. singlestoredb/management/cluster.py +462 -0
  78. singlestoredb/management/export.py +295 -0
  79. singlestoredb/management/files.py +1102 -0
  80. singlestoredb/management/inference_api.py +105 -0
  81. singlestoredb/management/job.py +887 -0
  82. singlestoredb/management/manager.py +373 -0
  83. singlestoredb/management/organization.py +226 -0
  84. singlestoredb/management/region.py +169 -0
  85. singlestoredb/management/utils.py +423 -0
  86. singlestoredb/management/workspace.py +1927 -0
  87. singlestoredb/mysql/__init__.py +177 -0
  88. singlestoredb/mysql/_auth.py +298 -0
  89. singlestoredb/mysql/charset.py +214 -0
  90. singlestoredb/mysql/connection.py +2032 -0
  91. singlestoredb/mysql/constants/CLIENT.py +38 -0
  92. singlestoredb/mysql/constants/COMMAND.py +32 -0
  93. singlestoredb/mysql/constants/CR.py +78 -0
  94. singlestoredb/mysql/constants/ER.py +474 -0
  95. singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
  96. singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
  97. singlestoredb/mysql/constants/FLAG.py +15 -0
  98. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  99. singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
  100. singlestoredb/mysql/constants/__init__.py +0 -0
  101. singlestoredb/mysql/converters.py +271 -0
  102. singlestoredb/mysql/cursors.py +896 -0
  103. singlestoredb/mysql/err.py +92 -0
  104. singlestoredb/mysql/optionfile.py +20 -0
  105. singlestoredb/mysql/protocol.py +450 -0
  106. singlestoredb/mysql/tests/__init__.py +19 -0
  107. singlestoredb/mysql/tests/base.py +126 -0
  108. singlestoredb/mysql/tests/conftest.py +37 -0
  109. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  110. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  111. singlestoredb/mysql/tests/test_basic.py +452 -0
  112. singlestoredb/mysql/tests/test_connection.py +851 -0
  113. singlestoredb/mysql/tests/test_converters.py +58 -0
  114. singlestoredb/mysql/tests/test_cursor.py +141 -0
  115. singlestoredb/mysql/tests/test_err.py +16 -0
  116. singlestoredb/mysql/tests/test_issues.py +514 -0
  117. singlestoredb/mysql/tests/test_load_local.py +75 -0
  118. singlestoredb/mysql/tests/test_nextset.py +88 -0
  119. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  120. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  121. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  122. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  123. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  124. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  125. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  126. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  127. singlestoredb/mysql/times.py +23 -0
  128. singlestoredb/notebook/__init__.py +16 -0
  129. singlestoredb/notebook/_objects.py +213 -0
  130. singlestoredb/notebook/_portal.py +352 -0
  131. singlestoredb/py.typed +0 -0
  132. singlestoredb/pytest.py +352 -0
  133. singlestoredb/server/__init__.py +0 -0
  134. singlestoredb/server/docker.py +452 -0
  135. singlestoredb/server/free_tier.py +267 -0
  136. singlestoredb/tests/__init__.py +0 -0
  137. singlestoredb/tests/alltypes.sql +307 -0
  138. singlestoredb/tests/alltypes_no_nulls.sql +208 -0
  139. singlestoredb/tests/empty.sql +0 -0
  140. singlestoredb/tests/ext_funcs/__init__.py +702 -0
  141. singlestoredb/tests/local_infile.csv +3 -0
  142. singlestoredb/tests/test.ipynb +18 -0
  143. singlestoredb/tests/test.sql +680 -0
  144. singlestoredb/tests/test2.ipynb +18 -0
  145. singlestoredb/tests/test2.sql +1 -0
  146. singlestoredb/tests/test_basics.py +1332 -0
  147. singlestoredb/tests/test_config.py +318 -0
  148. singlestoredb/tests/test_connection.py +3103 -0
  149. singlestoredb/tests/test_dbapi.py +27 -0
  150. singlestoredb/tests/test_exceptions.py +45 -0
  151. singlestoredb/tests/test_ext_func.py +1472 -0
  152. singlestoredb/tests/test_ext_func_data.py +1101 -0
  153. singlestoredb/tests/test_fusion.py +1527 -0
  154. singlestoredb/tests/test_http.py +288 -0
  155. singlestoredb/tests/test_management.py +1599 -0
  156. singlestoredb/tests/test_plugin.py +33 -0
  157. singlestoredb/tests/test_results.py +171 -0
  158. singlestoredb/tests/test_types.py +132 -0
  159. singlestoredb/tests/test_udf.py +737 -0
  160. singlestoredb/tests/test_udf_returns.py +459 -0
  161. singlestoredb/tests/test_vectorstore.py +51 -0
  162. singlestoredb/tests/test_xdict.py +333 -0
  163. singlestoredb/tests/utils.py +141 -0
  164. singlestoredb/types.py +373 -0
  165. singlestoredb/utils/__init__.py +0 -0
  166. singlestoredb/utils/config.py +950 -0
  167. singlestoredb/utils/convert_rows.py +69 -0
  168. singlestoredb/utils/debug.py +13 -0
  169. singlestoredb/utils/dtypes.py +205 -0
  170. singlestoredb/utils/events.py +65 -0
  171. singlestoredb/utils/mogrify.py +151 -0
  172. singlestoredb/utils/results.py +585 -0
  173. singlestoredb/utils/xdict.py +425 -0
  174. singlestoredb/vectorstore.py +192 -0
  175. singlestoredb/warnings.py +5 -0
  176. singlestoredb-1.16.1.dist-info/METADATA +165 -0
  177. singlestoredb-1.16.1.dist-info/RECORD +183 -0
  178. singlestoredb-1.16.1.dist-info/WHEEL +5 -0
  179. singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
  180. singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
  181. singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
  182. sqlx/__init__.py +4 -0
  183. sqlx/magic.py +113 -0
@@ -0,0 +1,459 @@
1
+ # mypy: disable-error-code="type-arg"
2
+ # from __future__ import annotations
3
+ import unittest
4
+ from typing import Any
5
+ from typing import Callable
6
+ from typing import List
7
+ from typing import NamedTuple
8
+ from typing import Optional
9
+ from typing import TypedDict
10
+
11
+ import numpy as np
12
+ import numpy.typing as npt
13
+ import pandas as pd
14
+ import polars as pl
15
+ import pyarrow as pa
16
+ from pydantic import BaseModel
17
+
18
+ from singlestoredb.functions import Table
19
+ from singlestoredb.functions import udf
20
+ from singlestoredb.functions.signature import get_signature
21
+ from singlestoredb.functions.signature import signature_to_sql
22
+
23
+
24
+ def to_sql(func: Callable[..., Any]) -> str:
25
+ """Convert a function signature to SQL."""
26
+ out = signature_to_sql(get_signature(func))
27
+ return out.split('EXTERNAL FUNCTION ')[1].split('AS REMOTE')[0].strip()
28
+
29
+
30
+ class Parameters(NamedTuple):
31
+ x: Optional[str] = ''
32
+
33
+
34
+ class UDFTuple(NamedTuple):
35
+ value: str
36
+
37
+
38
+ class TVFTuple(NamedTuple):
39
+ idx: int
40
+ value: Optional[str]
41
+
42
+
43
+ class TVFDict(TypedDict):
44
+ idx: int
45
+ value: Optional[str]
46
+
47
+
48
+ class TVFBaseModel(BaseModel):
49
+ idx: int
50
+ value: Optional[str]
51
+
52
+
53
+ class UDFResultsTest(unittest.TestCase):
54
+
55
+ def test_udf_returns(self) -> None:
56
+ # Plain UDF
57
+ @udf
58
+ def foo_a(x: str) -> str:
59
+ return f'0: {x}'
60
+
61
+ foo_a_out = foo_a('cat')
62
+
63
+ assert type(foo_a_out) is str
64
+ assert foo_a_out == '0: cat'
65
+ assert to_sql(foo_a) == '`foo_a`(`x` TEXT NOT NULL) RETURNS TEXT NOT NULL'
66
+
67
+ # Vectorized UDF using lists
68
+ @udf
69
+ def foo_b(x: List[str]) -> List[str]:
70
+ return [f'{i}: {y}' for i, y in enumerate(x)]
71
+
72
+ foo_b_out = foo_b(['cat', 'dog', 'monkey'])
73
+
74
+ assert type(foo_b_out) is list
75
+ assert foo_b_out == ['0: cat', '1: dog', '2: monkey']
76
+ assert to_sql(foo_b) == '`foo_b`(`x` TEXT NOT NULL) RETURNS TEXT NOT NULL'
77
+
78
+ # Illegal return type for UDF
79
+ @udf
80
+ def foo_c(x: List[str]) -> List[UDFTuple]:
81
+ return [UDFTuple(value='invalid')]
82
+
83
+ # Vectorized UDF using pandas Series
84
+ @udf(args=Parameters, returns=UDFTuple)
85
+ def foo_d(x: pd.Series) -> pd.Series:
86
+ return pd.Series([f'{i}: {y}' for i, y in enumerate(x)])
87
+
88
+ foo_d_out = foo_d(pd.Series(['cat', 'dog', 'monkey']))
89
+
90
+ assert type(foo_d_out) is pd.Series
91
+ assert list(foo_d_out) == ['0: cat', '1: dog', '2: monkey']
92
+ assert to_sql(foo_d) == "`foo_d`(`x` TEXT NULL DEFAULT '') RETURNS TEXT NOT NULL"
93
+
94
+ # Vectorized UDF using polars Series
95
+ @udf(args=Parameters, returns=UDFTuple)
96
+ def foo_e(x: pl.Series) -> pl.Series:
97
+ return pl.Series([f'{i}: {y}' for i, y in enumerate(x)])
98
+
99
+ foo_e_out = foo_e(pl.Series(['cat', 'dog', 'monkey']))
100
+
101
+ assert type(foo_e_out) is pl.Series
102
+ assert list(foo_e_out) == ['0: cat', '1: dog', '2: monkey']
103
+ assert to_sql(foo_e) == "`foo_e`(`x` TEXT NULL DEFAULT '') RETURNS TEXT NOT NULL"
104
+
105
+ # Vectorized UDF using numpy arrays
106
+ @udf(args=Parameters, returns=UDFTuple)
107
+ def foo_f(x: np.ndarray) -> np.ndarray:
108
+ return np.array([f'{i}: {y}' for i, y in enumerate(x)])
109
+
110
+ foo_f_out = foo_f(np.array(['cat', 'dog', 'monkey']))
111
+
112
+ assert type(foo_f_out) is np.ndarray
113
+ assert list(foo_f_out) == ['0: cat', '1: dog', '2: monkey']
114
+ assert to_sql(foo_f) == "`foo_f`(`x` TEXT NULL DEFAULT '') RETURNS TEXT NOT NULL"
115
+
116
+ # Vectorized UDF using typed numpy arrays
117
+ @udf
118
+ def foo_g(x: npt.NDArray[np.str_]) -> npt.NDArray[np.str_]:
119
+ return np.array([f'{i}: {y}' for i, y in enumerate(x)])
120
+
121
+ foo_g_out = foo_g(np.array(['cat', 'dog', 'monkey']))
122
+
123
+ assert type(foo_g_out) is np.ndarray
124
+ assert list(foo_g_out) == ['0: cat', '1: dog', '2: monkey']
125
+ assert to_sql(foo_g) == '`foo_g`(`x` TEXT NOT NULL) RETURNS TEXT NOT NULL'
126
+
127
+ # Plain TVF using one list
128
+ @udf
129
+ def foo_h_(x: str) -> Table[List[str]]:
130
+ return Table([x] * 3)
131
+
132
+ foo_h__out = foo_h_('cat')
133
+
134
+ assert type(foo_h__out) is Table
135
+ assert foo_h__out == Table(['cat', 'cat', 'cat'])
136
+
137
+ assert to_sql(foo_h_) == \
138
+ '`foo_h_`(`x` TEXT NOT NULL) RETURNS TABLE(`a` TEXT NOT NULL)'
139
+
140
+ # Plain TVF using multiple lists -- Illegal!
141
+ @udf
142
+ def foo_h(x: str) -> Table[List[int], List[str]]:
143
+ return Table(list(range(3)), [x] * 3)
144
+
145
+ foo_h_out = foo_h('cat')
146
+
147
+ assert type(foo_h_out) is Table
148
+ assert foo_h_out == Table([0, 1, 2], ['cat', 'cat', 'cat'])
149
+
150
+ with self.assertRaises(TypeError):
151
+ to_sql(foo_h)
152
+
153
+ # Plain TVF using lists of NamedTuples
154
+ @udf
155
+ def foo_i(x: str) -> Table[List[TVFTuple]]:
156
+ return Table([
157
+ TVFTuple(idx=0, value=x),
158
+ TVFTuple(idx=1, value=x),
159
+ TVFTuple(idx=2, value=x),
160
+ ])
161
+
162
+ foo_i_out = foo_i('cat')
163
+
164
+ assert type(foo_i_out) is Table
165
+ assert foo_i_out == Table([
166
+ TVFTuple(idx=0, value='cat'),
167
+ TVFTuple(idx=1, value='cat'),
168
+ TVFTuple(idx=2, value='cat'),
169
+ ])
170
+ assert to_sql(foo_i) == (
171
+ '`foo_i`(`x` TEXT NOT NULL) '
172
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
173
+ )
174
+
175
+ # Plain TVF using lists of TypedDicts
176
+ @udf
177
+ def foo_j(x: str) -> Table[List[TVFDict]]:
178
+ return Table([
179
+ dict(idx=0, value=x),
180
+ dict(idx=1, value=x),
181
+ dict(idx=2, value=x),
182
+ ])
183
+
184
+ foo_j_out = foo_j('cat')
185
+
186
+ assert type(foo_j_out) is Table
187
+ assert foo_j_out == Table([
188
+ dict(idx=0, value='cat'),
189
+ dict(idx=1, value='cat'),
190
+ dict(idx=2, value='cat'),
191
+ ])
192
+ assert to_sql(foo_j) == (
193
+ '`foo_j`(`x` TEXT NOT NULL) '
194
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
195
+ )
196
+
197
+ # Plain TVF using lists of pydantic BaseModels
198
+ @udf
199
+ def foo_k(x: str) -> Table[List[TVFBaseModel]]:
200
+ return Table([
201
+ TVFBaseModel(idx=0, value=x),
202
+ TVFBaseModel(idx=1, value=x),
203
+ TVFBaseModel(idx=2, value=x),
204
+ ])
205
+
206
+ foo_k_out = foo_k('cat')
207
+
208
+ assert type(foo_k_out) is Table
209
+ assert foo_k_out == Table([
210
+ TVFBaseModel(idx=0, value='cat'),
211
+ TVFBaseModel(idx=1, value='cat'),
212
+ TVFBaseModel(idx=2, value='cat'),
213
+ ])
214
+ assert to_sql(foo_k) == (
215
+ '`foo_k`(`x` TEXT NOT NULL) '
216
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
217
+ )
218
+
219
+ # Plain TVF using pandas Series
220
+ @udf(returns=TVFTuple)
221
+ def foo_l(x: str) -> Table[pd.Series, pd.Series]:
222
+ return Table(pd.Series(range(3)), pd.Series([x] * 3))
223
+
224
+ foo_l_out = foo_l('cat')
225
+
226
+ assert type(foo_l_out) is Table
227
+ assert len(foo_l_out) == 2
228
+ assert type(foo_l_out[0]) is pd.Series
229
+ assert list(foo_l_out[0]) == [0, 1, 2]
230
+ assert type(foo_l_out[1]) is pd.Series
231
+ assert list(foo_l_out[1]) == ['cat', 'cat', 'cat']
232
+ assert to_sql(foo_l) == (
233
+ '`foo_l`(`x` TEXT NOT NULL) '
234
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
235
+ )
236
+
237
+ # Plain TVF using polars Series
238
+ @udf(returns=TVFTuple)
239
+ def foo_m(x: str) -> Table[pl.Series, pl.Series]:
240
+ return Table(pl.Series(range(3)), pl.Series([x] * 3))
241
+
242
+ foo_m_out = foo_m('cat')
243
+
244
+ assert type(foo_m_out) is Table
245
+ assert len(foo_m_out) == 2
246
+ assert type(foo_m_out[0]) is pl.Series
247
+ assert list(foo_m_out[0]) == [0, 1, 2]
248
+ assert type(foo_m_out[1]) is pl.Series
249
+ assert list(foo_m_out[1]) == ['cat', 'cat', 'cat']
250
+ assert to_sql(foo_m) == (
251
+ '`foo_m`(`x` TEXT NOT NULL) '
252
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
253
+ )
254
+
255
+ # Plain TVF using pyarrow Array
256
+ @udf(returns=TVFTuple)
257
+ def foo_n(x: str) -> Table[pa.Array, pa.Array]:
258
+ return Table(pa.array(range(3)), pa.array([x] * 3))
259
+
260
+ foo_n_out = foo_n('cat')
261
+
262
+ assert type(foo_n_out) is Table
263
+ assert foo_n_out == Table(pa.array([0, 1, 2]), pa.array(['cat', 'cat', 'cat']))
264
+ assert to_sql(foo_n) == (
265
+ '`foo_n`(`x` TEXT NOT NULL) '
266
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
267
+ )
268
+
269
+ # Plain TVF using numpy arrays
270
+ @udf(returns=TVFTuple)
271
+ def foo_o(x: str) -> Table[np.ndarray, np.ndarray]:
272
+ return Table(np.array(range(3)), np.array([x] * 3))
273
+
274
+ foo_o_out = foo_o('cat')
275
+
276
+ assert type(foo_o_out) is Table
277
+ assert len(foo_o_out) == 2
278
+ assert type(foo_o_out[0]) is np.ndarray
279
+ assert list(foo_o_out[0]) == [0, 1, 2]
280
+ assert type(foo_o_out[1]) is np.ndarray
281
+ assert list(foo_o_out[1]) == ['cat', 'cat', 'cat']
282
+ assert to_sql(foo_o) == (
283
+ '`foo_o`(`x` TEXT NOT NULL) '
284
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
285
+ )
286
+
287
+ # Plain TVF using typed numpy arrays
288
+ @udf
289
+ def foo_p(x: str) -> Table[npt.NDArray[np.int_], npt.NDArray[np.str_]]:
290
+ return Table(np.array(range(3)), np.array([x] * 3))
291
+
292
+ foo_p_out = foo_p('cat')
293
+
294
+ assert type(foo_p_out) is Table
295
+ assert len(foo_p_out) == 2
296
+ assert type(foo_p_out[0]) is np.ndarray
297
+ assert list(foo_p_out[0]) == [0, 1, 2]
298
+ assert type(foo_p_out[1]) is np.ndarray
299
+ assert list(foo_p_out[1]) == ['cat', 'cat', 'cat']
300
+ assert to_sql(foo_p) == (
301
+ '`foo_p`(`x` TEXT NOT NULL) '
302
+ 'RETURNS TABLE(`a` BIGINT NOT NULL, `b` TEXT NOT NULL)'
303
+ )
304
+
305
+ # Plain TVF using pandas DataFrame
306
+ @udf(returns=TVFTuple)
307
+ def foo_q(x: str) -> Table[pd.DataFrame]:
308
+ return Table(pd.DataFrame([[0, x], [1, x], [2, x]])) # columns???
309
+
310
+ foo_q_out = foo_q('cat')
311
+
312
+ assert type(foo_q_out) is Table
313
+ assert len(foo_q_out) == 1
314
+ assert list(foo_q_out[0].iloc[:, 0]) == [0, 1, 2]
315
+ assert list(foo_q_out[0].iloc[:, 1]) == ['cat', 'cat', 'cat']
316
+ assert to_sql(foo_q) == (
317
+ '`foo_q`(`x` TEXT NOT NULL) '
318
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
319
+ )
320
+
321
+ # Plain TVF using polars DataFrame
322
+ @udf(returns=TVFTuple)
323
+ def foo_r(x: str) -> Table[pl.DataFrame]:
324
+ return Table(pl.DataFrame([[0, 1, 2], [x] * 3])) # columns???
325
+
326
+ foo_r_out = foo_r('cat')
327
+
328
+ assert type(foo_r_out) is Table
329
+ assert len(foo_r_out) == 1
330
+ assert list(foo_r_out[0][:, 0]) == [0, 1, 2]
331
+ assert list(foo_r_out[0][:, 1]) == ['cat', 'cat', 'cat']
332
+ assert to_sql(foo_r) == (
333
+ '`foo_r`(`x` TEXT NOT NULL) '
334
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
335
+ )
336
+
337
+ # Plain TVF using pyarrow Table
338
+ @udf(returns=TVFTuple)
339
+ def foo_s(x: str) -> Table[pa.Table]:
340
+ return Table(
341
+ pa.Table.from_pylist([
342
+ dict(idx=0, value='cat'),
343
+ dict(idx=1, value='cat'),
344
+ dict(idx=2, value='cat'),
345
+ ]),
346
+ ) # columns???
347
+
348
+ foo_s_out = foo_s('cat')
349
+
350
+ assert type(foo_s_out) is Table
351
+ assert foo_s_out == Table(
352
+ pa.Table.from_pylist([
353
+ dict(idx=0, value='cat'),
354
+ dict(idx=1, value='cat'),
355
+ dict(idx=2, value='cat'),
356
+ ]),
357
+ )
358
+ assert to_sql(foo_s) == (
359
+ '`foo_s`(`x` TEXT NOT NULL) '
360
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
361
+ )
362
+
363
+ # Vectorized TVF using lists -- Illegal!
364
+ @udf
365
+ def foo_t(x: List[str]) -> Table[List[int], List[str]]:
366
+ return Table(list(range(len(x))), x)
367
+
368
+ foo_t_out = foo_t(['cat', 'dog', 'monkey'])
369
+
370
+ assert type(foo_t_out) is Table
371
+ assert foo_t_out == Table([0, 1, 2], ['cat', 'dog', 'monkey'])
372
+ with self.assertRaises(TypeError):
373
+ to_sql(foo_t)
374
+
375
+ # Vectorized TVF using pandas Series
376
+ @udf(args=Parameters, returns=TVFTuple)
377
+ def foo_u(x: pd.Series) -> Table[pd.Series, pd.Series]:
378
+ return Table(pd.Series(range(len(x))), pd.Series(x))
379
+
380
+ foo_u_out = foo_u(pd.Series(['cat', 'dog', 'monkey']))
381
+
382
+ assert type(foo_u_out) is Table
383
+ assert len(foo_u_out) == 2
384
+ assert list(foo_u_out[0]) == [0, 1, 2]
385
+ assert list(foo_u_out[1]) == ['cat', 'dog', 'monkey']
386
+ assert to_sql(foo_u) == (
387
+ "`foo_u`(`x` TEXT NULL DEFAULT '') "
388
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
389
+ )
390
+
391
+ # Vectorized TVF using polars Series
392
+ @udf(args=Parameters, returns=TVFTuple)
393
+ def foo_v(x: pl.Series) -> Table[pl.Series, pl.Series]:
394
+ return Table(pl.Series(range(len(x))), pl.Series(x))
395
+
396
+ foo_v_out = foo_v(pl.Series(['cat', 'dog', 'monkey']))
397
+
398
+ assert type(foo_v_out) is Table
399
+ assert len(foo_v_out) == 2
400
+ assert list(foo_v_out[0]) == [0, 1, 2]
401
+ assert list(foo_v_out[1]) == ['cat', 'dog', 'monkey']
402
+ assert to_sql(foo_v) == (
403
+ "`foo_v`(`x` TEXT NULL DEFAULT '') "
404
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
405
+ )
406
+
407
+ # Vectorized TVF using pyarrow Array
408
+ @udf(args=Parameters, returns=TVFTuple)
409
+ def foo_w(x: pa.Array) -> Table[pa.Array, pa.Array]:
410
+ return Table(pa.array(range(len(x))), pa.array(x))
411
+
412
+ foo_w_out = foo_w(pa.array(['cat', 'dog', 'monkey']))
413
+
414
+ assert type(foo_w_out) is Table
415
+ assert foo_w_out == Table(
416
+ pa.array([0, 1, 2]), pa.array(['cat', 'dog', 'monkey']),
417
+ )
418
+ assert to_sql(foo_w) == (
419
+ "`foo_w`(`x` TEXT NULL DEFAULT '') "
420
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
421
+ )
422
+
423
+ # Vectorized TVF using numpy arrays
424
+ @udf(args=Parameters, returns=TVFTuple)
425
+ def foo_x(x: np.ndarray) -> Table[np.ndarray, np.ndarray]:
426
+ return Table(np.array(range(len(x))), np.array(x))
427
+
428
+ foo_x_out = foo_x(np.array(['cat', 'dog', 'monkey']))
429
+
430
+ assert type(foo_x_out) is Table
431
+ assert len(foo_x_out) == 2
432
+ assert list(foo_x_out[0]) == [0, 1, 2]
433
+ assert list(foo_x_out[1]) == ['cat', 'dog', 'monkey']
434
+ assert to_sql(foo_x) == (
435
+ "`foo_x`(`x` TEXT NULL DEFAULT '') "
436
+ 'RETURNS TABLE(`idx` BIGINT NOT NULL, `value` TEXT NULL)'
437
+ )
438
+
439
+ # Vectorized TVF using typed numpy arrays
440
+ @udf
441
+ def foo_y(
442
+ x: npt.NDArray[np.str_],
443
+ ) -> Table[npt.NDArray[np.int_], npt.NDArray[np.str_]]:
444
+ return Table(np.array(range(len(x))), np.array(x))
445
+
446
+ foo_y_out = foo_y(np.array(['cat', 'dog', 'monkey']))
447
+
448
+ assert type(foo_y_out) is Table
449
+ assert len(foo_y_out) == 2
450
+ assert list(foo_y_out[0]) == [0, 1, 2]
451
+ assert list(foo_y_out[1]) == ['cat', 'dog', 'monkey']
452
+ assert to_sql(foo_y) == (
453
+ '`foo_y`(`x` TEXT NOT NULL) '
454
+ 'RETURNS TABLE(`a` BIGINT NOT NULL, `b` TEXT NOT NULL)'
455
+ )
456
+
457
+
458
+ if __name__ == '__main__':
459
+ unittest.main()
@@ -0,0 +1,51 @@
1
+ import os
2
+ import unittest
3
+
4
+ from vectorstore import VectorDB
5
+
6
+ import singlestoredb as s2
7
+ from . import utils
8
+
9
+
10
+ class TestVectorDB(unittest.TestCase):
11
+
12
+ driver = s2
13
+
14
+ dbname: str = ''
15
+ dbexisted: bool = False
16
+
17
+ @classmethod
18
+ def setUpClass(cls) -> None:
19
+ sql_file = os.path.join(os.path.dirname(__file__), 'empty.sql')
20
+ cls.dbname, cls.dbexisted = utils.load_sql(sql_file) # type: ignore
21
+
22
+ @classmethod
23
+ def tearDownClass(cls) -> None:
24
+ if not cls.dbexisted:
25
+ utils.drop_database(cls.dbname) # type: ignore
26
+
27
+ def test_vectordb_from_params(self) -> None:
28
+ db: VectorDB = s2.vector_db(database=type(self).dbname)
29
+ index = db.create_index(
30
+ name='test_index', dimension=3,
31
+ tags={'name': 'test_tag'},
32
+ )
33
+ assert index.name == 'test_index'
34
+ assert index.dimension == 3
35
+ assert index.tags == {'name': 'test_tag'}
36
+ assert db.has_index('test_index')
37
+
38
+ def test_vectordb_from_connection(self) -> None:
39
+ with s2.connect(database=type(self).dbname) as conn:
40
+ db: VectorDB = conn.vector_db
41
+ index = db.create_index(
42
+ name='test_index_1',
43
+ dimension=4, tags={'name': 'test_tag'},
44
+ )
45
+ assert index.name == 'test_index_1'
46
+ assert index.dimension == 4
47
+ assert index.tags == {'name': 'test_tag'}
48
+ assert db.has_index('test_index_1')
49
+
50
+ db2: VectorDB = conn.vector_db
51
+ assert db2.has_index('test_index_1')