singlestoredb 0.4.0__py3-none-any.whl → 1.0.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.

Files changed (120) hide show
  1. singlestoredb/__init__.py +33 -1
  2. singlestoredb/alchemy/__init__.py +90 -0
  3. singlestoredb/auth.py +5 -1
  4. singlestoredb/config.py +116 -14
  5. singlestoredb/connection.py +483 -516
  6. singlestoredb/converters.py +238 -135
  7. singlestoredb/exceptions.py +30 -2
  8. singlestoredb/functions/__init__.py +1 -0
  9. singlestoredb/functions/decorator.py +142 -0
  10. singlestoredb/functions/dtypes.py +1639 -0
  11. singlestoredb/functions/ext/__init__.py +2 -0
  12. singlestoredb/functions/ext/arrow.py +375 -0
  13. singlestoredb/functions/ext/asgi.py +661 -0
  14. singlestoredb/functions/ext/json.py +427 -0
  15. singlestoredb/functions/ext/mmap.py +306 -0
  16. singlestoredb/functions/ext/rowdat_1.py +744 -0
  17. singlestoredb/functions/signature.py +673 -0
  18. singlestoredb/fusion/__init__.py +11 -0
  19. singlestoredb/fusion/graphql.py +213 -0
  20. singlestoredb/fusion/handler.py +621 -0
  21. singlestoredb/fusion/handlers/stage.py +257 -0
  22. singlestoredb/fusion/handlers/utils.py +162 -0
  23. singlestoredb/fusion/handlers/workspace.py +412 -0
  24. singlestoredb/fusion/registry.py +164 -0
  25. singlestoredb/fusion/result.py +399 -0
  26. singlestoredb/http/__init__.py +27 -0
  27. singlestoredb/{http.py → http/connection.py} +555 -154
  28. singlestoredb/management/__init__.py +3 -0
  29. singlestoredb/management/billing_usage.py +148 -0
  30. singlestoredb/management/cluster.py +14 -6
  31. singlestoredb/management/manager.py +100 -38
  32. singlestoredb/management/organization.py +188 -0
  33. singlestoredb/management/region.py +5 -5
  34. singlestoredb/management/utils.py +281 -2
  35. singlestoredb/management/workspace.py +1344 -49
  36. singlestoredb/{clients/pymysqlsv → mysql}/__init__.py +16 -21
  37. singlestoredb/{clients/pymysqlsv → mysql}/_auth.py +39 -8
  38. singlestoredb/{clients/pymysqlsv → mysql}/charset.py +26 -23
  39. singlestoredb/{clients/pymysqlsv/connections.py → mysql/connection.py} +532 -165
  40. singlestoredb/{clients/pymysqlsv → mysql}/constants/CLIENT.py +0 -1
  41. singlestoredb/{clients/pymysqlsv → mysql}/constants/COMMAND.py +0 -1
  42. singlestoredb/{clients/pymysqlsv → mysql}/constants/CR.py +0 -2
  43. singlestoredb/{clients/pymysqlsv → mysql}/constants/ER.py +0 -1
  44. singlestoredb/{clients/pymysqlsv → mysql}/constants/FIELD_TYPE.py +1 -1
  45. singlestoredb/{clients/pymysqlsv → mysql}/constants/FLAG.py +0 -1
  46. singlestoredb/{clients/pymysqlsv → mysql}/constants/SERVER_STATUS.py +0 -1
  47. singlestoredb/mysql/converters.py +271 -0
  48. singlestoredb/{clients/pymysqlsv → mysql}/cursors.py +228 -112
  49. singlestoredb/mysql/err.py +92 -0
  50. singlestoredb/{clients/pymysqlsv → mysql}/optionfile.py +5 -4
  51. singlestoredb/{clients/pymysqlsv → mysql}/protocol.py +49 -20
  52. singlestoredb/mysql/tests/__init__.py +19 -0
  53. singlestoredb/{clients/pymysqlsv → mysql}/tests/base.py +32 -12
  54. singlestoredb/mysql/tests/conftest.py +37 -0
  55. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_DictCursor.py +11 -7
  56. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_SSCursor.py +17 -12
  57. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_basic.py +32 -24
  58. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_connection.py +130 -119
  59. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_converters.py +9 -7
  60. singlestoredb/mysql/tests/test_cursor.py +141 -0
  61. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_err.py +3 -2
  62. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_issues.py +35 -27
  63. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_load_local.py +13 -11
  64. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_nextset.py +7 -3
  65. singlestoredb/{clients/pymysqlsv → mysql}/tests/test_optionfile.py +2 -1
  66. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/__init__.py +1 -1
  67. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  68. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/capabilities.py +19 -17
  69. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/dbapi20.py +31 -22
  70. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +3 -4
  71. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +24 -20
  72. singlestoredb/{clients/pymysqlsv → mysql}/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +4 -4
  73. singlestoredb/{clients/pymysqlsv → mysql}/times.py +3 -4
  74. singlestoredb/pytest.py +283 -0
  75. singlestoredb/tests/empty.sql +0 -0
  76. singlestoredb/tests/ext_funcs/__init__.py +385 -0
  77. singlestoredb/tests/test.sql +210 -0
  78. singlestoredb/tests/test2.sql +1 -0
  79. singlestoredb/tests/test_basics.py +482 -115
  80. singlestoredb/tests/test_config.py +13 -13
  81. singlestoredb/tests/test_connection.py +241 -305
  82. singlestoredb/tests/test_dbapi.py +27 -0
  83. singlestoredb/tests/test_ext_func.py +1193 -0
  84. singlestoredb/tests/test_ext_func_data.py +1101 -0
  85. singlestoredb/tests/test_fusion.py +465 -0
  86. singlestoredb/tests/test_http.py +32 -26
  87. singlestoredb/tests/test_management.py +588 -8
  88. singlestoredb/tests/test_plugin.py +33 -0
  89. singlestoredb/tests/test_results.py +11 -12
  90. singlestoredb/tests/test_udf.py +687 -0
  91. singlestoredb/tests/utils.py +3 -2
  92. singlestoredb/utils/config.py +58 -0
  93. singlestoredb/utils/debug.py +13 -0
  94. singlestoredb/utils/mogrify.py +151 -0
  95. singlestoredb/utils/results.py +4 -1
  96. singlestoredb-1.0.4.dist-info/METADATA +139 -0
  97. singlestoredb-1.0.4.dist-info/RECORD +112 -0
  98. {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/WHEEL +1 -1
  99. singlestoredb-1.0.4.dist-info/entry_points.txt +2 -0
  100. singlestoredb/clients/pymysqlsv/converters.py +0 -365
  101. singlestoredb/clients/pymysqlsv/err.py +0 -144
  102. singlestoredb/clients/pymysqlsv/tests/__init__.py +0 -19
  103. singlestoredb/clients/pymysqlsv/tests/test_cursor.py +0 -133
  104. singlestoredb/clients/pymysqlsv/tests/thirdparty/test_MySQLdb/__init__.py +0 -9
  105. singlestoredb/drivers/__init__.py +0 -45
  106. singlestoredb/drivers/base.py +0 -198
  107. singlestoredb/drivers/cymysql.py +0 -38
  108. singlestoredb/drivers/http.py +0 -47
  109. singlestoredb/drivers/mariadb.py +0 -40
  110. singlestoredb/drivers/mysqlconnector.py +0 -49
  111. singlestoredb/drivers/mysqldb.py +0 -60
  112. singlestoredb/drivers/pymysql.py +0 -37
  113. singlestoredb/drivers/pymysqlsv.py +0 -35
  114. singlestoredb/drivers/pyodbc.py +0 -65
  115. singlestoredb-0.4.0.dist-info/METADATA +0 -111
  116. singlestoredb-0.4.0.dist-info/RECORD +0 -86
  117. /singlestoredb/{clients → fusion/handlers}/__init__.py +0 -0
  118. /singlestoredb/{clients/pymysqlsv → mysql}/constants/__init__.py +0 -0
  119. {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/LICENSE +0 -0
  120. {singlestoredb-0.4.0.dist-info → singlestoredb-1.0.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1193 @@
1
+ #!/usr/bin/env python
2
+ # type: ignore
3
+ """Test SingleStoreDB external functions."""
4
+ import os
5
+ import socket
6
+ import subprocess
7
+ import time
8
+ import unittest
9
+
10
+ import requests
11
+
12
+ import singlestoredb as s2
13
+ import singlestoredb.mysql.constants.FIELD_TYPE as ft
14
+ from . import ext_funcs
15
+ from . import utils
16
+ from singlestoredb.functions.ext import create_app
17
+
18
+
19
+ try:
20
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
21
+ s.connect(('8.8.8.8', 80))
22
+ HTTP_HOST = s.getsockname()[0]
23
+ except Exception:
24
+ HTTP_HOST = '127.0.0.1'
25
+ finally:
26
+ s.close()
27
+
28
+
29
+ def get_open_port() -> int:
30
+ """Find an open port number."""
31
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
32
+ s.bind(('', 0))
33
+ s.listen(1)
34
+ port = s.getsockname()[1]
35
+ s.close()
36
+ return port
37
+
38
+
39
+ def start_http_server(database, data_format='rowdat_1'):
40
+ """Start an external function server."""
41
+ port = get_open_port()
42
+ print(f'Start UDF HTTP server on http://{HTTP_HOST}:{port}')
43
+ proc = subprocess.Popen(
44
+ ['uvicorn', 'singlestoredb.functions.ext:create_app'],
45
+ env=dict(
46
+ PATH=os.environ['PATH'],
47
+ PYTHONPATH=os.environ.get('PYTHONPATH', ''),
48
+ UVICORN_HOST=str(HTTP_HOST),
49
+ UVICORN_PORT=str(port),
50
+ UVICORN_FACTORY='1',
51
+ SINGLESTOREDB_EXT_FUNCTIONS='singlestoredb.tests.ext_funcs',
52
+ SINGLESTOREDB_PURE_PYTHON=os.environ.get('SINGLESTOREDB_PURE_PYTHON', '0'),
53
+ ),
54
+ )
55
+
56
+ # Wait for server to be available
57
+ retries = 10
58
+ while retries > 0:
59
+ try:
60
+ out = requests.get(f'http://{HTTP_HOST}:{port}/show/create_function')
61
+ if out.status_code == 200:
62
+ break
63
+ except Exception:
64
+ pass
65
+ time.sleep(3)
66
+ retries -= 1
67
+
68
+ app = create_app(
69
+ ext_funcs,
70
+ url=f'http://{HTTP_HOST}:{port}/invoke',
71
+ data_format=data_format,
72
+ )
73
+ app.register_functions(
74
+ database=database,
75
+ )
76
+
77
+ with s2.connect(database=database) as conn:
78
+ with conn.cursor() as cur:
79
+ cur.execute('set global enable_external_functions=on')
80
+ cur.execute('show functions')
81
+ for item in list(cur):
82
+ cur.execute(f'show create function `{item[0]}`')
83
+ for func in list(cur):
84
+ print(*func)
85
+
86
+ return proc, HTTP_HOST, port
87
+
88
+
89
+ def stop_http_server(proc, database):
90
+ """Stop the external function server."""
91
+ proc.terminate()
92
+ app = create_app(ext_funcs)
93
+ app.drop_functions(database=database)
94
+
95
+
96
+ class TestExtFunc(unittest.TestCase):
97
+
98
+ dbname: str = ''
99
+ dbexisted: bool = False
100
+ http_server = None
101
+ http_host = '127.0.0.1'
102
+ http_port = 0
103
+
104
+ @classmethod
105
+ def setUpClass(cls):
106
+ sql_file = os.path.join(os.path.dirname(__file__), 'test.sql')
107
+ cls.dbname, cls.dbexisted = utils.load_sql(sql_file)
108
+ cls.http_server, cls.http_host, cls.http_port = \
109
+ start_http_server(cls.dbname, 'rowdat_1')
110
+
111
+ @classmethod
112
+ def tearDownClass(cls):
113
+ stop_http_server(cls.http_server, cls.dbname)
114
+ cls.http_server = None
115
+ cls.http_host = '127.0.0.1'
116
+ cls.http_port = 0
117
+ if not cls.dbexisted:
118
+ utils.drop_database(cls.dbname)
119
+
120
+ def setUp(self):
121
+ self.conn = s2.connect(database=type(self).dbname)
122
+ self.cur = self.conn.cursor()
123
+
124
+ def tearDown(self):
125
+ try:
126
+ if self.cur is not None:
127
+ self.cur.close()
128
+ except Exception:
129
+ # traceback.print_exc()
130
+ pass
131
+
132
+ try:
133
+ if self.conn is not None:
134
+ self.conn.close()
135
+ except Exception:
136
+ # traceback.print_exc()
137
+ pass
138
+
139
+ def test_show_create_function(self):
140
+ out = requests.get(
141
+ f'http://{type(self).http_host}:{type(self).http_port}'
142
+ '/show/create_function',
143
+ )
144
+ print(out.text)
145
+
146
+ def test_double_mult(self):
147
+ self.cur.execute('select double_mult(value, 100) as res from data order by id')
148
+
149
+ assert [tuple(x) for x in self.cur] == \
150
+ [(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]
151
+
152
+ desc = self.cur.description
153
+ assert len(desc) == 1
154
+ assert desc[0].name == 'res'
155
+ assert desc[0].type_code == ft.DOUBLE
156
+ assert desc[0].null_ok is False
157
+
158
+ # NULL is not valid
159
+ with self.assertRaises(self.conn.OperationalError):
160
+ self.cur.execute(
161
+ 'select double_mult(value, NULL) as res '
162
+ 'from data order by id',
163
+ )
164
+
165
+ def test_pandas_double_mult(self):
166
+ self.cur.execute(
167
+ 'select pandas_double_mult(value, 100) as res '
168
+ 'from data order by id',
169
+ )
170
+
171
+ assert [tuple(x) for x in self.cur] == \
172
+ [(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]
173
+
174
+ desc = self.cur.description
175
+ assert len(desc) == 1
176
+ assert desc[0].name == 'res'
177
+ assert desc[0].type_code == ft.DOUBLE
178
+ assert desc[0].null_ok is False
179
+
180
+ # NULL is not valid
181
+ with self.assertRaises(self.conn.OperationalError):
182
+ self.cur.execute(
183
+ 'select pandas_double_mult(value, NULL) as res '
184
+ 'from data order by id',
185
+ )
186
+
187
+ def test_numpy_double_mult(self):
188
+ self.cur.execute(
189
+ 'select numpy_double_mult(value, 100) as res '
190
+ 'from data order by id',
191
+ )
192
+
193
+ assert [tuple(x) for x in self.cur] == \
194
+ [(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]
195
+
196
+ desc = self.cur.description
197
+ assert len(desc) == 1
198
+ assert desc[0].name == 'res'
199
+ assert desc[0].type_code == ft.DOUBLE
200
+ assert desc[0].null_ok is False
201
+
202
+ # NULL is not valid
203
+ with self.assertRaises(self.conn.OperationalError):
204
+ self.cur.execute(
205
+ 'select numpy_double_mult(value, NULL) as res '
206
+ 'from data order by id',
207
+ )
208
+
209
+ def test_arrow_double_mult(self):
210
+ self.cur.execute(
211
+ 'select arrow_double_mult(value, 100) as res '
212
+ 'from data order by id',
213
+ )
214
+
215
+ assert [tuple(x) for x in self.cur] == \
216
+ [(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]
217
+
218
+ desc = self.cur.description
219
+ assert len(desc) == 1
220
+ assert desc[0].name == 'res'
221
+ assert desc[0].type_code == ft.DOUBLE
222
+ assert desc[0].null_ok is False
223
+
224
+ # NULL is not valid
225
+ with self.assertRaises(self.conn.OperationalError):
226
+ self.cur.execute(
227
+ 'select arrow_double_mult(value, NULL) as res '
228
+ 'from data order by id',
229
+ )
230
+
231
+ def test_polars_double_mult(self):
232
+ self.cur.execute(
233
+ 'select polars_double_mult(value, 100) as res '
234
+ 'from data order by id',
235
+ )
236
+
237
+ assert [tuple(x) for x in self.cur] == \
238
+ [(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]
239
+
240
+ desc = self.cur.description
241
+ assert len(desc) == 1
242
+ assert desc[0].name == 'res'
243
+ assert desc[0].type_code == ft.DOUBLE
244
+ assert desc[0].null_ok is False
245
+
246
+ # NULL is not valid
247
+ with self.assertRaises(self.conn.OperationalError):
248
+ self.cur.execute(
249
+ 'select polars_double_mult(value, NULL) as res '
250
+ 'from data order by id',
251
+ )
252
+
253
+ def test_nullable_double_mult(self):
254
+ self.cur.execute(
255
+ 'select nullable_double_mult(value, 100) as res from '
256
+ 'data_with_nulls order by id',
257
+ )
258
+
259
+ assert [tuple(x) for x in self.cur] == \
260
+ [(200.0,), (200.0,), (500.0,), (None,), (0.0,)]
261
+
262
+ desc = self.cur.description
263
+ assert len(desc) == 1
264
+ assert desc[0].name == 'res'
265
+ assert desc[0].type_code == ft.DOUBLE
266
+ assert desc[0].null_ok is True
267
+
268
+ self.cur.execute(
269
+ 'select nullable_double_mult(value, NULL) as res '
270
+ 'from data_with_nulls order by id',
271
+ )
272
+
273
+ assert [tuple(x) for x in self.cur] == \
274
+ [(None,), (None,), (None,), (None,), (None,)]
275
+
276
+ def test_float_mult(self):
277
+ self.cur.execute(
278
+ 'select float_mult(value, 100) as res '
279
+ 'from data order by id',
280
+ )
281
+
282
+ assert [tuple(x) for x in self.cur] == \
283
+ [(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]
284
+
285
+ desc = self.cur.description
286
+ assert len(desc) == 1
287
+ assert desc[0].name == 'res'
288
+ assert desc[0].type_code == ft.FLOAT
289
+ assert desc[0].null_ok is False
290
+
291
+ # NULL is not valid
292
+ with self.assertRaises(self.conn.OperationalError):
293
+ self.cur.execute(
294
+ 'select float_mult(value, NULL) as res '
295
+ 'from data order by id',
296
+ )
297
+
298
+ def test_nullable_float_mult(self):
299
+ self.cur.execute(
300
+ 'select nullable_float_mult(value, 100) as res '
301
+ 'from data_with_nulls order by id',
302
+ )
303
+
304
+ assert [tuple(x) for x in self.cur] == \
305
+ [(200.0,), (200.0,), (500.0,), (None,), (0.0,)]
306
+
307
+ desc = self.cur.description
308
+ assert len(desc) == 1
309
+ assert desc[0].name == 'res'
310
+ assert desc[0].type_code == ft.FLOAT
311
+ assert desc[0].null_ok is True
312
+
313
+ self.cur.execute(
314
+ 'select nullable_float_mult(value, NULL) as res '
315
+ 'from data_with_nulls order by id',
316
+ )
317
+
318
+ assert [tuple(x) for x in self.cur] == \
319
+ [(None,), (None,), (None,), (None,), (None,)]
320
+
321
+ def test_int_mult(self):
322
+ self.cur.execute(
323
+ 'select int_mult(value, 100) as res '
324
+ 'from data order by id',
325
+ )
326
+
327
+ assert [tuple(x) for x in self.cur] == \
328
+ [(200,), (200,), (500,), (400,), (0,)]
329
+
330
+ desc = self.cur.description
331
+ assert len(desc) == 1
332
+ assert desc[0].name == 'res'
333
+ assert desc[0].type_code == ft.LONGLONG
334
+ assert desc[0].null_ok is False
335
+
336
+ # NULL is not valid
337
+ with self.assertRaises(self.conn.OperationalError):
338
+ self.cur.execute(
339
+ 'select int_mult(value, NULL) as res '
340
+ 'from data order by id',
341
+ )
342
+
343
+ def test_tinyint_mult(self):
344
+ self.cur.execute(
345
+ 'select tinyint_mult(value, 100) as res '
346
+ 'from data order by id',
347
+ )
348
+
349
+ assert [tuple(x) for x in self.cur] == \
350
+ [(127,), (127,), (127,), (127,), (0,)]
351
+
352
+ desc = self.cur.description
353
+ assert len(desc) == 1
354
+ assert desc[0].name == 'res'
355
+ assert desc[0].type_code == ft.TINY
356
+ assert desc[0].null_ok is False
357
+
358
+ # NULL is not valid
359
+ with self.assertRaises(self.conn.OperationalError):
360
+ self.cur.execute(
361
+ 'select tinyint_mult(value, NULL) as res '
362
+ 'from data order by id',
363
+ )
364
+
365
+ def test_pandas_tinyint_mult(self):
366
+ self.cur.execute(
367
+ 'select pandas_tinyint_mult(value, 100) as res '
368
+ 'from data order by id',
369
+ )
370
+
371
+ assert [tuple(x) for x in self.cur] == \
372
+ [(127,), (127,), (127,), (127,), (0,)]
373
+
374
+ desc = self.cur.description
375
+ assert len(desc) == 1
376
+ assert desc[0].name == 'res'
377
+ assert desc[0].type_code == ft.TINY
378
+ assert desc[0].null_ok is False
379
+
380
+ # NULL is not valid
381
+ with self.assertRaises(self.conn.OperationalError):
382
+ self.cur.execute(
383
+ 'select pandas_tinyint_mult(value, NULL) as res '
384
+ 'from data order by id',
385
+ )
386
+
387
+ def test_polars_tinyint_mult(self):
388
+ self.cur.execute(
389
+ 'select polars_tinyint_mult(value, 100) as res '
390
+ 'from data order by id',
391
+ )
392
+
393
+ assert [tuple(x) for x in self.cur] == \
394
+ [(127,), (127,), (127,), (127,), (0,)]
395
+
396
+ desc = self.cur.description
397
+ assert len(desc) == 1
398
+ assert desc[0].name == 'res'
399
+ assert desc[0].type_code == ft.TINY
400
+ assert desc[0].null_ok is False
401
+
402
+ # NULL is not valid
403
+ with self.assertRaises(self.conn.OperationalError):
404
+ self.cur.execute(
405
+ 'select polars_tinyint_mult(value, NULL) as res '
406
+ 'from data order by id',
407
+ )
408
+
409
+ def test_numpy_tinyint_mult(self):
410
+ self.cur.execute(
411
+ 'select numpy_tinyint_mult(value, 100) as res '
412
+ 'from data order by id',
413
+ )
414
+
415
+ assert [tuple(x) for x in self.cur] == \
416
+ [(127,), (127,), (127,), (127,), (0,)]
417
+
418
+ desc = self.cur.description
419
+ assert len(desc) == 1
420
+ assert desc[0].name == 'res'
421
+ assert desc[0].type_code == ft.TINY
422
+ assert desc[0].null_ok is False
423
+
424
+ # NULL is not valid
425
+ with self.assertRaises(self.conn.OperationalError):
426
+ self.cur.execute(
427
+ 'select numpy_tinyint_mult(value, NULL) as res '
428
+ 'from data order by id',
429
+ )
430
+
431
+ def test_arrow_tinyint_mult(self):
432
+ self.cur.execute(
433
+ 'select arrow_tinyint_mult(value, 100) as res from '
434
+ 'data order by id',
435
+ )
436
+
437
+ assert [tuple(x) for x in self.cur] == \
438
+ [(127,), (127,), (127,), (127,), (0,)]
439
+
440
+ desc = self.cur.description
441
+ assert len(desc) == 1
442
+ assert desc[0].name == 'res'
443
+ assert desc[0].type_code == ft.TINY
444
+ assert desc[0].null_ok is False
445
+
446
+ # NULL is not valid
447
+ with self.assertRaises(self.conn.OperationalError):
448
+ self.cur.execute(
449
+ 'select arrow_tinyint_mult(value, NULL) as res '
450
+ 'from data order by id',
451
+ )
452
+
453
+ def test_nullable_tinyint_mult(self):
454
+ self.cur.execute(
455
+ 'select nullable_tinyint_mult(value, 100) as res from '
456
+ 'data_with_nulls order by id',
457
+ )
458
+
459
+ assert [tuple(x) for x in self.cur] == \
460
+ [(127,), (127,), (127,), (None,), (0,)]
461
+
462
+ desc = self.cur.description
463
+ assert len(desc) == 1
464
+ assert desc[0].name == 'res'
465
+ assert desc[0].type_code == ft.TINY
466
+ assert desc[0].null_ok is True
467
+
468
+ def test_pandas_nullable_tinyint_mult(self):
469
+ self.cur.execute(
470
+ 'select pandas_nullable_tinyint_mult(value, 100) as res '
471
+ 'from data_with_nulls order by id',
472
+ )
473
+
474
+ assert [tuple(x) for x in self.cur] == \
475
+ [(127,), (127,), (127,), (0,), (0,)]
476
+
477
+ desc = self.cur.description
478
+ assert len(desc) == 1
479
+ assert desc[0].name == 'res'
480
+ assert desc[0].type_code == ft.TINY
481
+ assert desc[0].null_ok is True
482
+
483
+ def test_pandas_nullable_tinyint_mult_with_masks(self):
484
+ self.cur.execute(
485
+ 'select pandas_nullable_tinyint_mult_with_masks(value, 100) '
486
+ 'as res from data_with_nulls order by id',
487
+ )
488
+
489
+ assert [tuple(x) for x in self.cur] == \
490
+ [(127,), (127,), (127,), (None,), (0,)]
491
+
492
+ desc = self.cur.description
493
+ assert len(desc) == 1
494
+ assert desc[0].name == 'res'
495
+ assert desc[0].type_code == ft.TINY
496
+ assert desc[0].null_ok is True
497
+
498
+ def test_polars_nullable_tinyint_mult(self):
499
+ self.cur.execute(
500
+ 'select polars_nullable_tinyint_mult(value, 100) as res '
501
+ 'from data_with_nulls order by id',
502
+ )
503
+
504
+ assert [tuple(x) for x in self.cur] == \
505
+ [(127,), (127,), (127,), (0,), (0,)]
506
+
507
+ desc = self.cur.description
508
+ assert len(desc) == 1
509
+ assert desc[0].name == 'res'
510
+ assert desc[0].type_code == ft.TINY
511
+ assert desc[0].null_ok is True
512
+
513
+ def test_polars_nullable_tinyint_mult_with_masks(self):
514
+ self.cur.execute(
515
+ 'select polars_nullable_tinyint_mult_with_masks(value, 100) '
516
+ 'as res from data_with_nulls order by id',
517
+ )
518
+
519
+ assert [tuple(x) for x in self.cur] == \
520
+ [(127,), (127,), (127,), (None,), (0,)]
521
+
522
+ desc = self.cur.description
523
+ assert len(desc) == 1
524
+ assert desc[0].name == 'res'
525
+ assert desc[0].type_code == ft.TINY
526
+ assert desc[0].null_ok is True
527
+
528
+ def test_numpy_nullable_tinyint_mult(self):
529
+ self.cur.execute(
530
+ 'select numpy_nullable_tinyint_mult(value, 100) as res '
531
+ 'from data_with_nulls order by id',
532
+ )
533
+
534
+ assert [tuple(x) for x in self.cur] == \
535
+ [(127,), (127,), (127,), (0,), (0,)]
536
+
537
+ desc = self.cur.description
538
+ assert len(desc) == 1
539
+ assert desc[0].name == 'res'
540
+ assert desc[0].type_code == ft.TINY
541
+ assert desc[0].null_ok is True
542
+
543
+ def test_numpy_nullable_tinyint_mult_with_masks(self):
544
+ self.cur.execute(
545
+ 'select numpy_nullable_tinyint_mult_with_masks(value, 100) '
546
+ 'as res from data_with_nulls order by id',
547
+ )
548
+
549
+ assert [tuple(x) for x in self.cur] == \
550
+ [(127,), (127,), (127,), (None,), (0,)]
551
+
552
+ desc = self.cur.description
553
+ assert len(desc) == 1
554
+ assert desc[0].name == 'res'
555
+ assert desc[0].type_code == ft.TINY
556
+ assert desc[0].null_ok is True
557
+
558
+ def test_arrow_nullable_tinyint_mult(self):
559
+ self.cur.execute(
560
+ 'select arrow_nullable_tinyint_mult(value, 100) as res '
561
+ 'from data_with_nulls order by id',
562
+ )
563
+
564
+ assert [tuple(x) for x in self.cur] == \
565
+ [(127,), (127,), (127,), (None,), (0,)]
566
+
567
+ desc = self.cur.description
568
+ assert len(desc) == 1
569
+ assert desc[0].name == 'res'
570
+ assert desc[0].type_code == ft.TINY
571
+ assert desc[0].null_ok is True
572
+
573
+ def test_arrow_nullable_tinyint_mult_with_masks(self):
574
+ self.cur.execute(
575
+ 'select arrow_nullable_tinyint_mult_with_masks(value, 100) '
576
+ 'as res from data_with_nulls order by id',
577
+ )
578
+
579
+ assert [tuple(x) for x in self.cur] == \
580
+ [(127,), (127,), (127,), (None,), (0,)]
581
+
582
+ desc = self.cur.description
583
+ assert len(desc) == 1
584
+ assert desc[0].name == 'res'
585
+ assert desc[0].type_code == ft.TINY
586
+ assert desc[0].null_ok is True
587
+
588
+ def test_smallint_mult(self):
589
+ self.cur.execute(
590
+ 'select smallint_mult(value, 100) as res '
591
+ 'from data order by id',
592
+ )
593
+
594
+ assert [tuple(x) for x in self.cur] == \
595
+ [(200,), (200,), (500,), (400,), (0,)]
596
+
597
+ desc = self.cur.description
598
+ assert len(desc) == 1
599
+ assert desc[0].name == 'res'
600
+ assert desc[0].type_code == ft.SHORT
601
+ assert desc[0].null_ok is False
602
+
603
+ # NULL is not valid
604
+ with self.assertRaises(self.conn.OperationalError):
605
+ self.cur.execute(
606
+ 'select smallint_mult(value, NULL) as res '
607
+ 'from data order by id',
608
+ )
609
+
610
+ def test_pandas_smallint_mult(self):
611
+ self.cur.execute(
612
+ 'select pandas_smallint_mult(value, 100) as res '
613
+ 'from data order by id',
614
+ )
615
+
616
+ assert [tuple(x) for x in self.cur] == \
617
+ [(200,), (200,), (500,), (400,), (0,)]
618
+
619
+ desc = self.cur.description
620
+ assert len(desc) == 1
621
+ assert desc[0].name == 'res'
622
+ assert desc[0].type_code == ft.SHORT
623
+ assert desc[0].null_ok is False
624
+
625
+ # NULL is not valid
626
+ with self.assertRaises(self.conn.OperationalError):
627
+ self.cur.execute(
628
+ 'select pandas_smallint_mult(value, NULL) as res '
629
+ 'from data order by id',
630
+ )
631
+
632
+ def test_polars_smallint_mult(self):
633
+ self.cur.execute(
634
+ 'select polars_smallint_mult(value, 100) as res '
635
+ 'from data order by id',
636
+ )
637
+
638
+ assert [tuple(x) for x in self.cur] == \
639
+ [(200,), (200,), (500,), (400,), (0,)]
640
+
641
+ desc = self.cur.description
642
+ assert len(desc) == 1
643
+ assert desc[0].name == 'res'
644
+ assert desc[0].type_code == ft.SHORT
645
+ assert desc[0].null_ok is False
646
+
647
+ # NULL is not valid
648
+ with self.assertRaises(self.conn.OperationalError):
649
+ self.cur.execute(
650
+ 'select polars_smallint_mult(value, NULL) as res '
651
+ 'from data order by id',
652
+ )
653
+
654
+ def test_numpy_smallint_mult(self):
655
+ self.cur.execute(
656
+ 'select numpy_smallint_mult(value, 100) as res '
657
+ 'from data order by id',
658
+ )
659
+
660
+ assert [tuple(x) for x in self.cur] == \
661
+ [(200,), (200,), (500,), (400,), (0,)]
662
+
663
+ desc = self.cur.description
664
+ assert len(desc) == 1
665
+ assert desc[0].name == 'res'
666
+ assert desc[0].type_code == ft.SHORT
667
+ assert desc[0].null_ok is False
668
+
669
+ # NULL is not valid
670
+ with self.assertRaises(self.conn.OperationalError):
671
+ self.cur.execute(
672
+ 'select numpy_smallint_mult(value, NULL) as res '
673
+ 'from data order by id',
674
+ )
675
+
676
+ def test_arrow_smallint_mult(self):
677
+ self.cur.execute(
678
+ 'select arrow_smallint_mult(value, 100) as res '
679
+ 'from data order by id',
680
+ )
681
+
682
+ assert [tuple(x) for x in self.cur] == \
683
+ [(200,), (200,), (500,), (400,), (0,)]
684
+
685
+ desc = self.cur.description
686
+ assert len(desc) == 1
687
+ assert desc[0].name == 'res'
688
+ assert desc[0].type_code == ft.SHORT
689
+ assert desc[0].null_ok is False
690
+
691
+ # NULL is not valid
692
+ with self.assertRaises(self.conn.OperationalError):
693
+ self.cur.execute(
694
+ 'select arrow_smallint_mult(value, NULL) as res '
695
+ 'from data order by id',
696
+ )
697
+
698
+ def test_nullable_smallint_mult(self):
699
+ self.cur.execute(
700
+ 'select nullable_smallint_mult(value, 100) as res '
701
+ 'from data_with_nulls order by id',
702
+ )
703
+
704
+ assert [tuple(x) for x in self.cur] == \
705
+ [(200,), (200,), (500,), (None,), (0,)]
706
+
707
+ desc = self.cur.description
708
+ assert len(desc) == 1
709
+ assert desc[0].name == 'res'
710
+ assert desc[0].type_code == ft.SHORT
711
+ assert desc[0].null_ok is True
712
+
713
+ def test_mediumint_mult(self):
714
+ self.cur.execute(
715
+ 'select mediumint_mult(value, 100) as res '
716
+ 'from data order by id',
717
+ )
718
+
719
+ assert [tuple(x) for x in self.cur] == \
720
+ [(200,), (200,), (500,), (400,), (0,)]
721
+
722
+ desc = self.cur.description
723
+ assert len(desc) == 1
724
+ assert desc[0].name == 'res'
725
+ assert desc[0].type_code == ft.INT24
726
+ assert desc[0].null_ok is False
727
+
728
+ # NULL is not valid
729
+ with self.assertRaises(self.conn.OperationalError):
730
+ self.cur.execute(
731
+ 'select mediumint_mult(value, NULL) as res '
732
+ 'from data order by id',
733
+ )
734
+
735
+ def test_pandas_mediumint_mult(self):
736
+ self.cur.execute(
737
+ 'select pandas_mediumint_mult(value, 100) as res '
738
+ 'from data order by id',
739
+ )
740
+
741
+ assert [tuple(x) for x in self.cur] == \
742
+ [(200,), (200,), (500,), (400,), (0,)]
743
+
744
+ desc = self.cur.description
745
+ assert len(desc) == 1
746
+ assert desc[0].name == 'res'
747
+ assert desc[0].type_code == ft.INT24
748
+ assert desc[0].null_ok is False
749
+
750
+ # NULL is not valid
751
+ with self.assertRaises(self.conn.OperationalError):
752
+ self.cur.execute(
753
+ 'select pandas_mediumint_mult(value, NULL) as res '
754
+ 'from data order by id',
755
+ )
756
+
757
+ def test_polars_mediumint_mult(self):
758
+ self.cur.execute(
759
+ 'select polars_mediumint_mult(value, 100) as res '
760
+ 'from data order by id',
761
+ )
762
+
763
+ assert [tuple(x) for x in self.cur] == \
764
+ [(200,), (200,), (500,), (400,), (0,)]
765
+
766
+ desc = self.cur.description
767
+ assert len(desc) == 1
768
+ assert desc[0].name == 'res'
769
+ assert desc[0].type_code == ft.INT24
770
+ assert desc[0].null_ok is False
771
+
772
+ # NULL is not valid
773
+ with self.assertRaises(self.conn.OperationalError):
774
+ self.cur.execute(
775
+ 'select polars_mediumint_mult(value, NULL) as res '
776
+ 'from data order by id',
777
+ )
778
+
779
+ def test_numpy_mediumint_mult(self):
780
+ self.cur.execute(
781
+ 'select numpy_mediumint_mult(value, 100) as res '
782
+ 'from data order by id',
783
+ )
784
+
785
+ assert [tuple(x) for x in self.cur] == \
786
+ [(200,), (200,), (500,), (400,), (0,)]
787
+
788
+ desc = self.cur.description
789
+ assert len(desc) == 1
790
+ assert desc[0].name == 'res'
791
+ assert desc[0].type_code == ft.INT24
792
+ assert desc[0].null_ok is False
793
+
794
+ # NULL is not valid
795
+ with self.assertRaises(self.conn.OperationalError):
796
+ self.cur.execute(
797
+ 'select numpy_mediumint_mult(value, NULL) as res '
798
+ 'from data order by id',
799
+ )
800
+
801
+ def test_arrow_mediumint_mult(self):
802
+ self.cur.execute(
803
+ 'select arrow_mediumint_mult(value, 100) as res '
804
+ 'from data order by id',
805
+ )
806
+
807
+ assert [tuple(x) for x in self.cur] == \
808
+ [(200,), (200,), (500,), (400,), (0,)]
809
+
810
+ desc = self.cur.description
811
+ assert len(desc) == 1
812
+ assert desc[0].name == 'res'
813
+ assert desc[0].type_code == ft.INT24
814
+ assert desc[0].null_ok is False
815
+
816
+ # NULL is not valid
817
+ with self.assertRaises(self.conn.OperationalError):
818
+ self.cur.execute(
819
+ 'select arrow_mediumint_mult(value, NULL) as res '
820
+ 'from data order by id',
821
+ )
822
+
823
+ def test_nullable_mediumint_mult(self):
824
+ self.cur.execute(
825
+ 'select nullable_mediumint_mult(value, 100) as res '
826
+ 'from data_with_nulls order by id',
827
+ )
828
+
829
+ assert [tuple(x) for x in self.cur] == \
830
+ [(200,), (200,), (500,), (None,), (0,)]
831
+
832
+ desc = self.cur.description
833
+ assert len(desc) == 1
834
+ assert desc[0].name == 'res'
835
+ assert desc[0].type_code == ft.INT24
836
+ assert desc[0].null_ok is True
837
+
838
+ def test_bigint_mult(self):
839
+ self.cur.execute(
840
+ 'select bigint_mult(value, 100) as res '
841
+ 'from data order by id',
842
+ )
843
+
844
+ assert [tuple(x) for x in self.cur] == \
845
+ [(200,), (200,), (500,), (400,), (0,)]
846
+
847
+ desc = self.cur.description
848
+ assert len(desc) == 1
849
+ assert desc[0].name == 'res'
850
+ assert desc[0].type_code == ft.LONGLONG
851
+ assert desc[0].null_ok is False
852
+
853
+ # NULL is not valid
854
+ with self.assertRaises(self.conn.OperationalError):
855
+ self.cur.execute(
856
+ 'select bigint_mult(value, NULL) as res '
857
+ 'from data order by id',
858
+ )
859
+
860
+ def test_pandas_bigint_mult(self):
861
+ self.cur.execute(
862
+ 'select pandas_bigint_mult(value, 100) as res '
863
+ 'from data order by id',
864
+ )
865
+
866
+ assert [tuple(x) for x in self.cur] == \
867
+ [(200,), (200,), (500,), (400,), (0,)]
868
+
869
+ desc = self.cur.description
870
+ assert len(desc) == 1
871
+ assert desc[0].name == 'res'
872
+ assert desc[0].type_code == ft.LONGLONG
873
+ assert desc[0].null_ok is False
874
+
875
+ # NULL is not valid
876
+ with self.assertRaises(self.conn.OperationalError):
877
+ self.cur.execute(
878
+ 'select pandas_bigint_mult(value, NULL) as res '
879
+ 'from data order by id',
880
+ )
881
+
882
+ def test_polars_bigint_mult(self):
883
+ self.cur.execute(
884
+ 'select polars_bigint_mult(value, 100) as res '
885
+ 'from data order by id',
886
+ )
887
+
888
+ assert [tuple(x) for x in self.cur] == \
889
+ [(200,), (200,), (500,), (400,), (0,)]
890
+
891
+ desc = self.cur.description
892
+ assert len(desc) == 1
893
+ assert desc[0].name == 'res'
894
+ assert desc[0].type_code == ft.LONGLONG
895
+ assert desc[0].null_ok is False
896
+
897
+ # NULL is not valid
898
+ with self.assertRaises(self.conn.OperationalError):
899
+ self.cur.execute(
900
+ 'select polars_bigint_mult(value, NULL) as res '
901
+ 'from data order by id',
902
+ )
903
+
904
+ def test_numpy_bigint_mult(self):
905
+ self.cur.execute(
906
+ 'select numpy_bigint_mult(value, 100) as res '
907
+ 'from data order by id',
908
+ )
909
+
910
+ assert [tuple(x) for x in self.cur] == \
911
+ [(200,), (200,), (500,), (400,), (0,)]
912
+
913
+ desc = self.cur.description
914
+ assert len(desc) == 1
915
+ assert desc[0].name == 'res'
916
+ assert desc[0].type_code == ft.LONGLONG
917
+ assert desc[0].null_ok is False
918
+
919
+ # NULL is not valid
920
+ with self.assertRaises(self.conn.OperationalError):
921
+ self.cur.execute(
922
+ 'select numpy_bigint_mult(value, NULL) as res '
923
+ 'from data order by id',
924
+ )
925
+
926
+ def test_numpy_nullable_bigint_mult(self):
927
+ self.cur.execute(
928
+ 'select numpy_nullable_bigint_mult(value, 100) as res '
929
+ 'from data_with_nulls order by id',
930
+ )
931
+
932
+ assert [tuple(x) for x in self.cur] == \
933
+ [(200,), (200,), (500,), (None,), (0,)]
934
+
935
+ desc = self.cur.description
936
+ assert len(desc) == 1
937
+ assert desc[0].name == 'res'
938
+ assert desc[0].type_code == ft.LONGLONG
939
+ assert desc[0].null_ok is True
940
+
941
+ def test_arrow_bigint_mult(self):
942
+ self.cur.execute(
943
+ 'select arrow_bigint_mult(value, 100) as res '
944
+ 'from data order by id',
945
+ )
946
+
947
+ assert [tuple(x) for x in self.cur] == \
948
+ [(200,), (200,), (500,), (400,), (0,)]
949
+
950
+ desc = self.cur.description
951
+ assert len(desc) == 1
952
+ assert desc[0].name == 'res'
953
+ assert desc[0].type_code == ft.LONGLONG
954
+ assert desc[0].null_ok is False
955
+
956
+ # NULL is not valid
957
+ with self.assertRaises(self.conn.OperationalError):
958
+ self.cur.execute(
959
+ 'select arrow_bigint_mult(value, NULL) as res '
960
+ 'from data order by id',
961
+ )
962
+
963
+ def test_nullable_bigint_mult(self):
964
+ self.cur.execute(
965
+ 'select nullable_bigint_mult(value, 100) as res '
966
+ 'from data_with_nulls order by id',
967
+ )
968
+
969
+ assert [tuple(x) for x in self.cur] == \
970
+ [(200,), (200,), (500,), (None,), (0,)]
971
+
972
+ desc = self.cur.description
973
+ assert len(desc) == 1
974
+ assert desc[0].name == 'res'
975
+ assert desc[0].type_code == ft.LONGLONG
976
+ assert desc[0].null_ok is True
977
+
978
+ def test_nullable_int_mult(self):
979
+ self.cur.execute(
980
+ 'select nullable_int_mult(value, 100) as res '
981
+ 'from data_with_nulls order by id',
982
+ )
983
+
984
+ assert [tuple(x) for x in self.cur] == \
985
+ [(200,), (200,), (500,), (None,), (0,)]
986
+
987
+ desc = self.cur.description
988
+ assert len(desc) == 1
989
+ assert desc[0].name == 'res'
990
+ assert desc[0].type_code == ft.LONGLONG
991
+ assert desc[0].null_ok is True
992
+
993
+ def test_string_mult(self):
994
+ self.cur.execute(
995
+ 'select string_mult(name, value) as res '
996
+ 'from data order by id',
997
+ )
998
+
999
+ assert [tuple(x) for x in self.cur] == [
1000
+ ('antelopesantelopes',),
1001
+ ('bearsbears',),
1002
+ ('catscatscatscatscats',),
1003
+ ('dogsdogsdogsdogs',),
1004
+ ('',),
1005
+ ]
1006
+
1007
+ desc = self.cur.description
1008
+ assert len(desc) == 1
1009
+ assert desc[0].name == 'res'
1010
+ assert desc[0].type_code == ft.BLOB
1011
+ assert desc[0].null_ok is False
1012
+
1013
+ # NULL is not valid
1014
+ with self.assertRaises(self.conn.OperationalError):
1015
+ self.cur.execute(
1016
+ 'select string_mult(NULL, value) as res '
1017
+ 'from data order by id',
1018
+ )
1019
+
1020
+ def test_pandas_string_mult(self):
1021
+ self.cur.execute(
1022
+ 'select pandas_string_mult(name, value) as res '
1023
+ 'from data order by id',
1024
+ )
1025
+
1026
+ assert [tuple(x) for x in self.cur] == [
1027
+ ('antelopesantelopes',),
1028
+ ('bearsbears',),
1029
+ ('catscatscatscatscats',),
1030
+ ('dogsdogsdogsdogs',),
1031
+ ('',),
1032
+ ]
1033
+
1034
+ desc = self.cur.description
1035
+ assert len(desc) == 1
1036
+ assert desc[0].name == 'res'
1037
+ assert desc[0].type_code == ft.BLOB
1038
+ assert desc[0].null_ok is False
1039
+
1040
+ # NULL is not valid
1041
+ with self.assertRaises(self.conn.OperationalError):
1042
+ self.cur.execute(
1043
+ 'select pandas_string_mult(NULL, value) as res '
1044
+ 'from data order by id',
1045
+ )
1046
+
1047
+ def test_numpy_string_mult(self):
1048
+ self.cur.execute(
1049
+ 'select numpy_string_mult(name, value) as res '
1050
+ 'from data order by id',
1051
+ )
1052
+
1053
+ assert [tuple(x) for x in self.cur] == [
1054
+ ('antelopesantelopes',),
1055
+ ('bearsbears',),
1056
+ ('catscatscatscatscats',),
1057
+ ('dogsdogsdogsdogs',),
1058
+ ('',),
1059
+ ]
1060
+
1061
+ desc = self.cur.description
1062
+ assert len(desc) == 1
1063
+ assert desc[0].name == 'res'
1064
+ assert desc[0].type_code == ft.BLOB
1065
+ assert desc[0].null_ok is False
1066
+
1067
+ # NULL is not valid
1068
+ with self.assertRaises(self.conn.OperationalError):
1069
+ self.cur.execute(
1070
+ 'select numpy_string_mult(NULL, value) as res '
1071
+ 'from data order by id',
1072
+ )
1073
+
1074
+ def _test_polars_string_mult(self):
1075
+ self.cur.execute(
1076
+ 'select polars_string_mult(name, value) as res '
1077
+ 'from data order by id',
1078
+ )
1079
+
1080
+ assert [tuple(x) for x in self.cur] == [
1081
+ ('antelopesantelopes',),
1082
+ ('bearsbears',),
1083
+ ('catscatscatscatscats',),
1084
+ ('dogsdogsdogsdogs',),
1085
+ ('',),
1086
+ ]
1087
+
1088
+ desc = self.cur.description
1089
+ assert len(desc) == 1
1090
+ assert desc[0].name == 'res'
1091
+ assert desc[0].type_code == ft.BLOB
1092
+ assert desc[0].null_ok is False
1093
+
1094
+ # NULL is not valid
1095
+ with self.assertRaises(self.conn.OperationalError):
1096
+ self.cur.execute(
1097
+ 'select polars_string_mult(NULL, value) as res '
1098
+ 'from data order by id',
1099
+ )
1100
+
1101
+ def _test_arrow_string_mult(self):
1102
+ self.cur.execute(
1103
+ 'select arrow_string_mult(name, value) as res '
1104
+ 'from data order by id',
1105
+ )
1106
+
1107
+ assert [tuple(x) for x in self.cur] == [
1108
+ ('antelopesantelopes',),
1109
+ ('bearsbears',),
1110
+ ('catscatscatscatscats',),
1111
+ ('dogsdogsdogsdogs',),
1112
+ ('',),
1113
+ ]
1114
+
1115
+ desc = self.cur.description
1116
+ assert len(desc) == 1
1117
+ assert desc[0].name == 'res'
1118
+ assert desc[0].type_code == ft.BLOB
1119
+ assert desc[0].null_ok is False
1120
+
1121
+ # NULL is not valid
1122
+ with self.assertRaises(self.conn.OperationalError):
1123
+ self.cur.execute(
1124
+ 'select arrow_string_mult(NULL, value) as res '
1125
+ 'from data order by id',
1126
+ )
1127
+
1128
+ def test_nullable_string_mult(self):
1129
+ self.cur.execute(
1130
+ 'select nullable_string_mult(name, value) as res '
1131
+ 'from data_with_nulls order by id',
1132
+ )
1133
+
1134
+ assert [tuple(x) for x in self.cur] == [
1135
+ ('antelopesantelopes',),
1136
+ (None,),
1137
+ (None,),
1138
+ (None,),
1139
+ ('',),
1140
+ ]
1141
+
1142
+ desc = self.cur.description
1143
+ assert len(desc) == 1
1144
+ assert desc[0].name == 'res'
1145
+ assert desc[0].type_code == ft.BLOB
1146
+ assert desc[0].null_ok is True
1147
+
1148
+ def test_varchar_mult(self):
1149
+ self.cur.execute(
1150
+ 'select varchar_mult(name, value) as res '
1151
+ 'from data order by id',
1152
+ )
1153
+
1154
+ assert [tuple(x) for x in self.cur] == [
1155
+ ('antelopesantelopes',),
1156
+ ('bearsbears',),
1157
+ ('catscatscatscatscats',),
1158
+ ('dogsdogsdogsdogs',),
1159
+ ('',),
1160
+ ]
1161
+
1162
+ desc = self.cur.description
1163
+ assert len(desc) == 1
1164
+ assert desc[0].name == 'res'
1165
+ assert desc[0].type_code == ft.BLOB
1166
+ assert desc[0].null_ok is False
1167
+
1168
+ # NULL is not valid
1169
+ with self.assertRaises(self.conn.OperationalError):
1170
+ self.cur.execute(
1171
+ 'select varchar_mult(NULL, value) as res '
1172
+ 'from data order by id',
1173
+ )
1174
+
1175
+ def test_nullable_varchar_mult(self):
1176
+ self.cur.execute(
1177
+ 'select nullable_varchar_mult(name, value) as res '
1178
+ 'from data_with_nulls order by id',
1179
+ )
1180
+
1181
+ assert [tuple(x) for x in self.cur] == [
1182
+ ('antelopesantelopes',),
1183
+ (None,),
1184
+ (None,),
1185
+ (None,),
1186
+ ('',),
1187
+ ]
1188
+
1189
+ desc = self.cur.description
1190
+ assert len(desc) == 1
1191
+ assert desc[0].name == 'res'
1192
+ assert desc[0].type_code == ft.BLOB
1193
+ assert desc[0].null_ok is True