Path: blob/main/singlestoredb/tests/test_ext_func.py
801 views
#!/usr/bin/env python1# type: ignore2"""Test SingleStoreDB external functions."""3import os4import socket5import subprocess6import time7import unittest89import requests1011import singlestoredb as s212import singlestoredb.mysql.constants.FIELD_TYPE as ft13from . import ext_funcs14from . import utils15from singlestoredb.functions.ext.asgi import create_app161718try:19s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)20s.connect(('8.8.8.8', 80))21HTTP_HOST = s.getsockname()[0]22except Exception:23HTTP_HOST = '127.0.0.1'24finally:25s.close()262728def get_open_port() -> int:29"""Find an open port number."""30s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)31s.bind(('', 0))32s.listen(1)33port = s.getsockname()[1]34s.close()35return port363738def start_http_server(database, data_format='rowdat_1'):39"""Start an external function server."""40port = get_open_port()41print(f'Start UDF HTTP server on http://{HTTP_HOST}:{port}')42proc = subprocess.Popen(43['uvicorn', 'singlestoredb.functions.ext.asgi:create_app'],44env=dict(45PATH=os.environ['PATH'],46PYTHONPATH=os.environ.get('PYTHONPATH', ''),47UVICORN_HOST=str(HTTP_HOST),48UVICORN_PORT=str(port),49UVICORN_FACTORY='1',50SINGLESTOREDB_EXT_FUNCTIONS='singlestoredb.tests.ext_funcs',51SINGLESTOREDB_PURE_PYTHON=os.environ.get('SINGLESTOREDB_PURE_PYTHON', '0'),52),53)5455# Wait for server to be available56retries = 1057while retries > 0:58try:59out = requests.get(f'http://{HTTP_HOST}:{port}/show/create_function')60if out.status_code == 200:61break62except Exception:63pass64time.sleep(3)65retries -= 16667app = create_app(68ext_funcs,69url=f'http://{HTTP_HOST}:{port}/invoke',70data_format=data_format,71)72app.register_functions(73database=database,74)7576with s2.connect(database=database) as conn:77with conn.cursor() as cur:78cur.execute('set global enable_external_functions=on')79cur.execute('show functions')80for item in list(cur):81cur.execute(f'show create function `{item[0]}`')82for func in list(cur):83print(*func)8485return proc, HTTP_HOST, port868788def stop_http_server(proc, database):89"""Stop the external function server."""90proc.terminate()91app = create_app(ext_funcs)92app.drop_functions(database=database)939495class TestExtFunc(unittest.TestCase):9697dbname: str = ''98dbexisted: bool = False99http_server = None100http_host = '127.0.0.1'101http_port = 0102103@classmethod104def setUpClass(cls):105sql_file = os.path.join(os.path.dirname(__file__), 'test.sql')106cls.dbname, cls.dbexisted = utils.load_sql(sql_file)107cls.http_server, cls.http_host, cls.http_port = \108start_http_server(cls.dbname, 'rowdat_1')109110@classmethod111def tearDownClass(cls):112stop_http_server(cls.http_server, cls.dbname)113cls.http_server = None114cls.http_host = '127.0.0.1'115cls.http_port = 0116if not cls.dbexisted:117utils.drop_database(cls.dbname)118119def setUp(self):120self.conn = s2.connect(database=type(self).dbname)121self.cur = self.conn.cursor()122123def tearDown(self):124try:125if self.cur is not None:126self.cur.close()127except Exception:128# traceback.print_exc()129pass130131try:132if self.conn is not None:133self.conn.close()134except Exception:135# traceback.print_exc()136pass137138def test_show_create_function(self):139out = requests.get(140f'http://{type(self).http_host}:{type(self).http_port}'141'/show/create_function',142)143print(out.text)144145def test_double_mult(self):146self.cur.execute('select double_mult(value, 100) as res from data order by id')147148assert [tuple(x) for x in self.cur] == \149[(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]150151desc = self.cur.description152assert len(desc) == 1153assert desc[0].name == 'res'154assert desc[0].type_code == ft.DOUBLE155assert desc[0].null_ok is False156157# NULL is not valid158with self.assertRaises(self.conn.OperationalError):159self.cur.execute(160'select double_mult(value, NULL) as res '161'from data order by id',162)163164def test_timeout_double_mult(self):165with self.assertRaises(self.conn.OperationalError) as exc:166self.cur.execute(167'select timeout_double_mult(value, 100) as res '168'from longer_data order by id',169)170assert 'timeout' in str(exc.exception).lower()171172def test_async_double_mult(self):173self.cur.execute(174'select async_double_mult(value, 100) as res from data order by id',175)176177assert [tuple(x) for x in self.cur] == \178[(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]179180desc = self.cur.description181assert len(desc) == 1182assert desc[0].name == 'res'183assert desc[0].type_code == ft.DOUBLE184assert desc[0].null_ok is False185186# NULL is not valid187with self.assertRaises(self.conn.OperationalError):188self.cur.execute(189'select async_double_mult(value, NULL) as res '190'from data order by id',191)192193def test_async_timeout_double_mult(self):194with self.assertRaises(self.conn.OperationalError) as exc:195self.cur.execute(196'select async_timeout_double_mult(value, 100) as res '197'from longer_data order by id',198)199assert 'timeout' in str(exc.exception).lower()200201def test_pandas_double_mult(self):202self.cur.execute(203'select pandas_double_mult(value, 100) as res '204'from data order by id',205)206207assert [tuple(x) for x in self.cur] == \208[(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]209210desc = self.cur.description211assert len(desc) == 1212assert desc[0].name == 'res'213assert desc[0].type_code == ft.DOUBLE214assert desc[0].null_ok is False215216# NULL is not valid217with self.assertRaises(self.conn.OperationalError):218self.cur.execute(219'select pandas_double_mult(value, NULL) as res '220'from data order by id',221)222223def test_numpy_double_mult(self):224self.cur.execute(225'select numpy_double_mult(value, 100) as res '226'from data order by id',227)228229assert [tuple(x) for x in self.cur] == \230[(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]231232desc = self.cur.description233assert len(desc) == 1234assert desc[0].name == 'res'235assert desc[0].type_code == ft.DOUBLE236assert desc[0].null_ok is False237238# NULL is not valid239with self.assertRaises(self.conn.OperationalError):240self.cur.execute(241'select numpy_double_mult(value, NULL) as res '242'from data order by id',243)244245def test_async_numpy_double_mult(self):246self.cur.execute(247'select async_numpy_double_mult(value, 100) as res '248'from data order by id',249)250251assert [tuple(x) for x in self.cur] == \252[(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]253254desc = self.cur.description255assert len(desc) == 1256assert desc[0].name == 'res'257assert desc[0].type_code == ft.DOUBLE258assert desc[0].null_ok is False259260# NULL is not valid261with self.assertRaises(self.conn.OperationalError):262self.cur.execute(263'select async_numpy_double_mult(value, NULL) as res '264'from data order by id',265)266267def test_arrow_double_mult(self):268self.cur.execute(269'select arrow_double_mult(value, 100) as res '270'from data order by id',271)272273assert [tuple(x) for x in self.cur] == \274[(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]275276desc = self.cur.description277assert len(desc) == 1278assert desc[0].name == 'res'279assert desc[0].type_code == ft.DOUBLE280assert desc[0].null_ok is False281282# NULL is not valid283with self.assertRaises(self.conn.OperationalError):284self.cur.execute(285'select arrow_double_mult(value, NULL) as res '286'from data order by id',287)288289def test_polars_double_mult(self):290self.cur.execute(291'select polars_double_mult(value, 100) as res '292'from data order by id',293)294295assert [tuple(x) for x in self.cur] == \296[(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]297298desc = self.cur.description299assert len(desc) == 1300assert desc[0].name == 'res'301assert desc[0].type_code == ft.DOUBLE302assert desc[0].null_ok is False303304# NULL is not valid305with self.assertRaises(self.conn.OperationalError):306self.cur.execute(307'select polars_double_mult(value, NULL) as res '308'from data order by id',309)310311def test_nullable_double_mult(self):312self.cur.execute(313'select nullable_double_mult(value, 100) as res from '314'data_with_nulls order by id',315)316317assert [tuple(x) for x in self.cur] == \318[(200.0,), (200.0,), (500.0,), (None,), (0.0,)]319320desc = self.cur.description321assert len(desc) == 1322assert desc[0].name == 'res'323assert desc[0].type_code == ft.DOUBLE324assert desc[0].null_ok is True325326self.cur.execute(327'select nullable_double_mult(value, NULL) as res '328'from data_with_nulls order by id',329)330331assert [tuple(x) for x in self.cur] == \332[(None,), (None,), (None,), (None,), (None,)]333334def test_float_mult(self):335self.cur.execute(336'select float_mult(value, 100) as res '337'from data order by id',338)339340assert [tuple(x) for x in self.cur] == \341[(200.0,), (200.0,), (500.0,), (400.0,), (0.0,)]342343desc = self.cur.description344assert len(desc) == 1345assert desc[0].name == 'res'346assert desc[0].type_code == ft.FLOAT347assert desc[0].null_ok is False348349# NULL is not valid350with self.assertRaises(self.conn.OperationalError):351self.cur.execute(352'select float_mult(value, NULL) as res '353'from data order by id',354)355356def test_nullable_float_mult(self):357self.cur.execute(358'select nullable_float_mult(value, 100) as res '359'from data_with_nulls order by id',360)361362assert [tuple(x) for x in self.cur] == \363[(200.0,), (200.0,), (500.0,), (None,), (0.0,)]364365desc = self.cur.description366assert len(desc) == 1367assert desc[0].name == 'res'368assert desc[0].type_code == ft.FLOAT369assert desc[0].null_ok is True370371self.cur.execute(372'select nullable_float_mult(value, NULL) as res '373'from data_with_nulls order by id',374)375376assert [tuple(x) for x in self.cur] == \377[(None,), (None,), (None,), (None,), (None,)]378379def test_int_mult(self):380self.cur.execute(381'select int_mult(value, 100) as res '382'from data order by id',383)384385assert [tuple(x) for x in self.cur] == \386[(200,), (200,), (500,), (400,), (0,)]387388desc = self.cur.description389assert len(desc) == 1390assert desc[0].name == 'res'391assert desc[0].type_code == ft.LONGLONG392assert desc[0].null_ok is False393394# NULL is not valid395with self.assertRaises(self.conn.OperationalError):396self.cur.execute(397'select int_mult(value, NULL) as res '398'from data order by id',399)400401def test_tinyint_mult(self):402self.cur.execute(403'select tinyint_mult(value, 100) as res '404'from data order by id',405)406407assert [tuple(x) for x in self.cur] == \408[(127,), (127,), (127,), (127,), (0,)]409410desc = self.cur.description411assert len(desc) == 1412assert desc[0].name == 'res'413assert desc[0].type_code == ft.TINY414assert desc[0].null_ok is False415416# NULL is not valid417with self.assertRaises(self.conn.OperationalError):418self.cur.execute(419'select tinyint_mult(value, NULL) as res '420'from data order by id',421)422423def test_pandas_tinyint_mult(self):424self.cur.execute(425'select pandas_tinyint_mult(value, 100) as res '426'from data order by id',427)428429assert [tuple(x) for x in self.cur] == \430[(127,), (127,), (127,), (127,), (0,)]431432desc = self.cur.description433assert len(desc) == 1434assert desc[0].name == 'res'435assert desc[0].type_code == ft.TINY436assert desc[0].null_ok is False437438# NULL is not valid439with self.assertRaises(self.conn.OperationalError):440self.cur.execute(441'select pandas_tinyint_mult(value, NULL) as res '442'from data order by id',443)444445def test_polars_tinyint_mult(self):446self.cur.execute(447'select polars_tinyint_mult(value, 100) as res '448'from data order by id',449)450451assert [tuple(x) for x in self.cur] == \452[(127,), (127,), (127,), (127,), (0,)]453454desc = self.cur.description455assert len(desc) == 1456assert desc[0].name == 'res'457assert desc[0].type_code == ft.TINY458assert desc[0].null_ok is False459460# NULL is not valid461with self.assertRaises(self.conn.OperationalError):462self.cur.execute(463'select polars_tinyint_mult(value, NULL) as res '464'from data order by id',465)466467def test_numpy_tinyint_mult(self):468self.cur.execute(469'select numpy_tinyint_mult(value, 100) as res '470'from data order by id',471)472473assert [tuple(x) for x in self.cur] == \474[(127,), (127,), (127,), (127,), (0,)]475476desc = self.cur.description477assert len(desc) == 1478assert desc[0].name == 'res'479assert desc[0].type_code == ft.TINY480assert desc[0].null_ok is False481482# NULL is not valid483with self.assertRaises(self.conn.OperationalError):484self.cur.execute(485'select numpy_tinyint_mult(value, NULL) as res '486'from data order by id',487)488489def test_arrow_tinyint_mult(self):490self.cur.execute(491'select arrow_tinyint_mult(value, 100) as res from '492'data order by id',493)494495assert [tuple(x) for x in self.cur] == \496[(127,), (127,), (127,), (127,), (0,)]497498desc = self.cur.description499assert len(desc) == 1500assert desc[0].name == 'res'501assert desc[0].type_code == ft.TINY502assert desc[0].null_ok is False503504# NULL is not valid505with self.assertRaises(self.conn.OperationalError):506self.cur.execute(507'select arrow_tinyint_mult(value, NULL) as res '508'from data order by id',509)510511def test_nullable_tinyint_mult(self):512self.cur.execute(513'select nullable_tinyint_mult(value, 100) as res from '514'data_with_nulls order by id',515)516517assert [tuple(x) for x in self.cur] == \518[(127,), (127,), (127,), (None,), (0,)]519520desc = self.cur.description521assert len(desc) == 1522assert desc[0].name == 'res'523assert desc[0].type_code == ft.TINY524assert desc[0].null_ok is True525526def test_pandas_nullable_tinyint_mult(self):527self.cur.execute(528'select pandas_nullable_tinyint_mult(value, 100) as res '529'from data_with_nulls order by id',530)531532assert [tuple(x) for x in self.cur] == \533[(127,), (127,), (127,), (0,), (0,)]534535desc = self.cur.description536assert len(desc) == 1537assert desc[0].name == 'res'538assert desc[0].type_code == ft.TINY539assert desc[0].null_ok is True540541def test_pandas_nullable_tinyint_mult_with_masks(self):542self.cur.execute(543'select pandas_nullable_tinyint_mult_with_masks(value, 100) '544'as res from data_with_nulls order by id',545)546547assert [tuple(x) for x in self.cur] == \548[(127,), (127,), (127,), (None,), (0,)]549550desc = self.cur.description551assert len(desc) == 1552assert desc[0].name == 'res'553assert desc[0].type_code == ft.TINY554assert desc[0].null_ok is True555556def test_polars_nullable_tinyint_mult(self):557self.cur.execute(558'select polars_nullable_tinyint_mult(value, 100) as res '559'from data_with_nulls order by id',560)561562assert [tuple(x) for x in self.cur] == \563[(127,), (127,), (127,), (0,), (0,)]564565desc = self.cur.description566assert len(desc) == 1567assert desc[0].name == 'res'568assert desc[0].type_code == ft.TINY569assert desc[0].null_ok is True570571def test_polars_nullable_tinyint_mult_with_masks(self):572self.cur.execute(573'select polars_nullable_tinyint_mult_with_masks(value, 100) '574'as res from data_with_nulls order by id',575)576577assert [tuple(x) for x in self.cur] == \578[(127,), (127,), (127,), (None,), (0,)]579580desc = self.cur.description581assert len(desc) == 1582assert desc[0].name == 'res'583assert desc[0].type_code == ft.TINY584assert desc[0].null_ok is True585586def test_numpy_nullable_tinyint_mult(self):587self.cur.execute(588'select numpy_nullable_tinyint_mult(value, 100) as res '589'from data_with_nulls order by id',590)591592assert [tuple(x) for x in self.cur] == \593[(127,), (127,), (127,), (0,), (0,)]594595desc = self.cur.description596assert len(desc) == 1597assert desc[0].name == 'res'598assert desc[0].type_code == ft.TINY599assert desc[0].null_ok is True600601def test_numpy_nullable_tinyint_mult_with_masks(self):602self.cur.execute(603'select numpy_nullable_tinyint_mult_with_masks(value, 100) '604'as res from data_with_nulls order by id',605)606607assert [tuple(x) for x in self.cur] == \608[(127,), (127,), (127,), (None,), (0,)]609610desc = self.cur.description611assert len(desc) == 1612assert desc[0].name == 'res'613assert desc[0].type_code == ft.TINY614assert desc[0].null_ok is True615616def test_arrow_nullable_tinyint_mult(self):617self.cur.execute(618'select arrow_nullable_tinyint_mult(value, 100) as res '619'from data_with_nulls order by id',620)621622assert [tuple(x) for x in self.cur] == \623[(127,), (127,), (127,), (None,), (0,)]624625desc = self.cur.description626assert len(desc) == 1627assert desc[0].name == 'res'628assert desc[0].type_code == ft.TINY629assert desc[0].null_ok is True630631def test_arrow_nullable_tinyint_mult_with_masks(self):632self.cur.execute(633'select arrow_nullable_tinyint_mult_with_masks(value, 100) '634'as res from data_with_nulls order by id',635)636637assert [tuple(x) for x in self.cur] == \638[(127,), (127,), (127,), (None,), (0,)]639640desc = self.cur.description641assert len(desc) == 1642assert desc[0].name == 'res'643assert desc[0].type_code == ft.TINY644assert desc[0].null_ok is True645646def test_smallint_mult(self):647self.cur.execute(648'select smallint_mult(value, 100) as res '649'from data order by id',650)651652assert [tuple(x) for x in self.cur] == \653[(200,), (200,), (500,), (400,), (0,)]654655desc = self.cur.description656assert len(desc) == 1657assert desc[0].name == 'res'658assert desc[0].type_code == ft.SHORT659assert desc[0].null_ok is False660661# NULL is not valid662with self.assertRaises(self.conn.OperationalError):663self.cur.execute(664'select smallint_mult(value, NULL) as res '665'from data order by id',666)667668def test_pandas_smallint_mult(self):669self.cur.execute(670'select pandas_smallint_mult(value, 100) as res '671'from data order by id',672)673674assert [tuple(x) for x in self.cur] == \675[(200,), (200,), (500,), (400,), (0,)]676677desc = self.cur.description678assert len(desc) == 1679assert desc[0].name == 'res'680assert desc[0].type_code == ft.SHORT681assert desc[0].null_ok is False682683# NULL is not valid684with self.assertRaises(self.conn.OperationalError):685self.cur.execute(686'select pandas_smallint_mult(value, NULL) as res '687'from data order by id',688)689690def test_polars_smallint_mult(self):691self.cur.execute(692'select polars_smallint_mult(value, 100) as res '693'from data order by id',694)695696assert [tuple(x) for x in self.cur] == \697[(200,), (200,), (500,), (400,), (0,)]698699desc = self.cur.description700assert len(desc) == 1701assert desc[0].name == 'res'702assert desc[0].type_code == ft.SHORT703assert desc[0].null_ok is False704705# NULL is not valid706with self.assertRaises(self.conn.OperationalError):707self.cur.execute(708'select polars_smallint_mult(value, NULL) as res '709'from data order by id',710)711712def test_numpy_smallint_mult(self):713self.cur.execute(714'select numpy_smallint_mult(value, 100) as res '715'from data order by id',716)717718assert [tuple(x) for x in self.cur] == \719[(200,), (200,), (500,), (400,), (0,)]720721desc = self.cur.description722assert len(desc) == 1723assert desc[0].name == 'res'724assert desc[0].type_code == ft.SHORT725assert desc[0].null_ok is False726727# NULL is not valid728with self.assertRaises(self.conn.OperationalError):729self.cur.execute(730'select numpy_smallint_mult(value, NULL) as res '731'from data order by id',732)733734def test_arrow_smallint_mult(self):735self.cur.execute(736'select arrow_smallint_mult(value, 100) as res '737'from data order by id',738)739740assert [tuple(x) for x in self.cur] == \741[(200,), (200,), (500,), (400,), (0,)]742743desc = self.cur.description744assert len(desc) == 1745assert desc[0].name == 'res'746assert desc[0].type_code == ft.SHORT747assert desc[0].null_ok is False748749# NULL is not valid750with self.assertRaises(self.conn.OperationalError):751self.cur.execute(752'select arrow_smallint_mult(value, NULL) as res '753'from data order by id',754)755756def test_nullable_smallint_mult(self):757self.cur.execute(758'select nullable_smallint_mult(value, 100) as res '759'from data_with_nulls order by id',760)761762assert [tuple(x) for x in self.cur] == \763[(200,), (200,), (500,), (None,), (0,)]764765desc = self.cur.description766assert len(desc) == 1767assert desc[0].name == 'res'768assert desc[0].type_code == ft.SHORT769assert desc[0].null_ok is True770771def test_mediumint_mult(self):772self.cur.execute(773'select mediumint_mult(value, 100) as res '774'from data order by id',775)776777assert [tuple(x) for x in self.cur] == \778[(200,), (200,), (500,), (400,), (0,)]779780desc = self.cur.description781assert len(desc) == 1782assert desc[0].name == 'res'783assert desc[0].type_code == ft.INT24784assert desc[0].null_ok is False785786# NULL is not valid787with self.assertRaises(self.conn.OperationalError):788self.cur.execute(789'select mediumint_mult(value, NULL) as res '790'from data order by id',791)792793def test_pandas_mediumint_mult(self):794self.cur.execute(795'select pandas_mediumint_mult(value, 100) as res '796'from data order by id',797)798799assert [tuple(x) for x in self.cur] == \800[(200,), (200,), (500,), (400,), (0,)]801802desc = self.cur.description803assert len(desc) == 1804assert desc[0].name == 'res'805assert desc[0].type_code == ft.INT24806assert desc[0].null_ok is False807808# NULL is not valid809with self.assertRaises(self.conn.OperationalError):810self.cur.execute(811'select pandas_mediumint_mult(value, NULL) as res '812'from data order by id',813)814815def test_polars_mediumint_mult(self):816self.cur.execute(817'select polars_mediumint_mult(value, 100) as res '818'from data order by id',819)820821assert [tuple(x) for x in self.cur] == \822[(200,), (200,), (500,), (400,), (0,)]823824desc = self.cur.description825assert len(desc) == 1826assert desc[0].name == 'res'827assert desc[0].type_code == ft.INT24828assert desc[0].null_ok is False829830# NULL is not valid831with self.assertRaises(self.conn.OperationalError):832self.cur.execute(833'select polars_mediumint_mult(value, NULL) as res '834'from data order by id',835)836837def test_numpy_mediumint_mult(self):838self.cur.execute(839'select numpy_mediumint_mult(value, 100) as res '840'from data order by id',841)842843assert [tuple(x) for x in self.cur] == \844[(200,), (200,), (500,), (400,), (0,)]845846desc = self.cur.description847assert len(desc) == 1848assert desc[0].name == 'res'849assert desc[0].type_code == ft.INT24850assert desc[0].null_ok is False851852# NULL is not valid853with self.assertRaises(self.conn.OperationalError):854self.cur.execute(855'select numpy_mediumint_mult(value, NULL) as res '856'from data order by id',857)858859def test_arrow_mediumint_mult(self):860self.cur.execute(861'select arrow_mediumint_mult(value, 100) as res '862'from data order by id',863)864865assert [tuple(x) for x in self.cur] == \866[(200,), (200,), (500,), (400,), (0,)]867868desc = self.cur.description869assert len(desc) == 1870assert desc[0].name == 'res'871assert desc[0].type_code == ft.INT24872assert desc[0].null_ok is False873874# NULL is not valid875with self.assertRaises(self.conn.OperationalError):876self.cur.execute(877'select arrow_mediumint_mult(value, NULL) as res '878'from data order by id',879)880881def test_nullable_mediumint_mult(self):882self.cur.execute(883'select nullable_mediumint_mult(value, 100) as res '884'from data_with_nulls order by id',885)886887assert [tuple(x) for x in self.cur] == \888[(200,), (200,), (500,), (None,), (0,)]889890desc = self.cur.description891assert len(desc) == 1892assert desc[0].name == 'res'893assert desc[0].type_code == ft.INT24894assert desc[0].null_ok is True895896def test_bigint_mult(self):897self.cur.execute(898'select bigint_mult(value, 100) as res '899'from data order by id',900)901902assert [tuple(x) for x in self.cur] == \903[(200,), (200,), (500,), (400,), (0,)]904905desc = self.cur.description906assert len(desc) == 1907assert desc[0].name == 'res'908assert desc[0].type_code == ft.LONGLONG909assert desc[0].null_ok is False910911# NULL is not valid912with self.assertRaises(self.conn.OperationalError):913self.cur.execute(914'select bigint_mult(value, NULL) as res '915'from data order by id',916)917918def test_pandas_bigint_mult(self):919self.cur.execute(920'select pandas_bigint_mult(value, 100) as res '921'from data order by id',922)923924assert [tuple(x) for x in self.cur] == \925[(200,), (200,), (500,), (400,), (0,)]926927desc = self.cur.description928assert len(desc) == 1929assert desc[0].name == 'res'930assert desc[0].type_code == ft.LONGLONG931assert desc[0].null_ok is False932933# NULL is not valid934with self.assertRaises(self.conn.OperationalError):935self.cur.execute(936'select pandas_bigint_mult(value, NULL) as res '937'from data order by id',938)939940def test_polars_bigint_mult(self):941self.cur.execute(942'select polars_bigint_mult(value, 100) as res '943'from data order by id',944)945946assert [tuple(x) for x in self.cur] == \947[(200,), (200,), (500,), (400,), (0,)]948949desc = self.cur.description950assert len(desc) == 1951assert desc[0].name == 'res'952assert desc[0].type_code == ft.LONGLONG953assert desc[0].null_ok is False954955# NULL is not valid956with self.assertRaises(self.conn.OperationalError):957self.cur.execute(958'select polars_bigint_mult(value, NULL) as res '959'from data order by id',960)961962def test_numpy_bigint_mult(self):963self.cur.execute(964'select numpy_bigint_mult(value, 100) as res '965'from data order by id',966)967968assert [tuple(x) for x in self.cur] == \969[(200,), (200,), (500,), (400,), (0,)]970971desc = self.cur.description972assert len(desc) == 1973assert desc[0].name == 'res'974assert desc[0].type_code == ft.LONGLONG975assert desc[0].null_ok is False976977# NULL is not valid978with self.assertRaises(self.conn.OperationalError):979self.cur.execute(980'select numpy_bigint_mult(value, NULL) as res '981'from data order by id',982)983984def test_numpy_nullable_bigint_mult(self):985self.cur.execute(986'select numpy_nullable_bigint_mult(value, 100) as res '987'from data_with_nulls order by id',988)989990# assert [tuple(x) for x in self.cur] == \991# [(200,), (200,), (500,), (None,), (0,)]992assert [tuple(x) for x in self.cur] == \993[(200,), (200,), (500,), (0,), (0,)]994995desc = self.cur.description996assert len(desc) == 1997assert desc[0].name == 'res'998assert desc[0].type_code == ft.LONGLONG999assert desc[0].null_ok is True10001001def test_arrow_bigint_mult(self):1002self.cur.execute(1003'select arrow_bigint_mult(value, 100) as res '1004'from data order by id',1005)10061007assert [tuple(x) for x in self.cur] == \1008[(200,), (200,), (500,), (400,), (0,)]10091010desc = self.cur.description1011assert len(desc) == 11012assert desc[0].name == 'res'1013assert desc[0].type_code == ft.LONGLONG1014assert desc[0].null_ok is False10151016# NULL is not valid1017with self.assertRaises(self.conn.OperationalError):1018self.cur.execute(1019'select arrow_bigint_mult(value, NULL) as res '1020'from data order by id',1021)10221023def test_nullable_bigint_mult(self):1024self.cur.execute(1025'select nullable_bigint_mult(value, 100) as res '1026'from data_with_nulls order by id',1027)10281029assert [tuple(x) for x in self.cur] == \1030[(200,), (200,), (500,), (None,), (0,)]10311032desc = self.cur.description1033assert len(desc) == 11034assert desc[0].name == 'res'1035assert desc[0].type_code == ft.LONGLONG1036assert desc[0].null_ok is True10371038def test_nullable_int_mult(self):1039self.cur.execute(1040'select nullable_int_mult(value, 100) as res '1041'from data_with_nulls order by id',1042)10431044assert [tuple(x) for x in self.cur] == \1045[(200,), (200,), (500,), (None,), (0,)]10461047desc = self.cur.description1048assert len(desc) == 11049assert desc[0].name == 'res'1050assert desc[0].type_code == ft.LONGLONG1051assert desc[0].null_ok is True10521053# ========== BOOL TESTS ==========10541055def test_bool_and(self):1056"""Test scalar (non-vector) bool AND."""1057self.cur.execute('select bool_and(TRUE, TRUE) as res')1058assert [tuple(x) for x in self.cur] == [(1,)]10591060self.cur.execute('select bool_and(TRUE, FALSE) as res')1061assert [tuple(x) for x in self.cur] == [(0,)]10621063self.cur.execute('select bool_and(FALSE, TRUE) as res')1064assert [tuple(x) for x in self.cur] == [(0,)]10651066self.cur.execute('select bool_and(FALSE, FALSE) as res')1067assert [tuple(x) for x in self.cur] == [(0,)]10681069desc = self.cur.description1070assert len(desc) == 11071assert desc[0].name == 'res'1072assert desc[0].type_code == ft.TINY # BOOL is stored as TINYINT1073assert desc[0].null_ok is False10741075def test_bool_or(self):1076"""Test scalar (non-vector) bool OR."""1077self.cur.execute('select bool_or(TRUE, TRUE) as res')1078assert [tuple(x) for x in self.cur] == [(1,)]10791080self.cur.execute('select bool_or(TRUE, FALSE) as res')1081assert [tuple(x) for x in self.cur] == [(1,)]10821083self.cur.execute('select bool_or(FALSE, TRUE) as res')1084assert [tuple(x) for x in self.cur] == [(1,)]10851086self.cur.execute('select bool_or(FALSE, FALSE) as res')1087assert [tuple(x) for x in self.cur] == [(0,)]10881089desc = self.cur.description1090assert len(desc) == 11091assert desc[0].name == 'res'1092assert desc[0].type_code == ft.TINY1093assert desc[0].null_ok is False10941095def test_bool_not(self):1096"""Test scalar (non-vector) bool NOT."""1097self.cur.execute('select bool_not(TRUE) as res')1098assert [tuple(x) for x in self.cur] == [(0,)]10991100self.cur.execute('select bool_not(FALSE) as res')1101assert [tuple(x) for x in self.cur] == [(1,)]11021103desc = self.cur.description1104assert len(desc) == 11105assert desc[0].name == 'res'1106assert desc[0].type_code == ft.TINY1107assert desc[0].null_ok is False11081109def test_bool_xor(self):1110"""Test scalar (non-vector) bool XOR."""1111self.cur.execute('select bool_xor(TRUE, TRUE) as res')1112assert [tuple(x) for x in self.cur] == [(0,)]11131114self.cur.execute('select bool_xor(TRUE, FALSE) as res')1115assert [tuple(x) for x in self.cur] == [(1,)]11161117self.cur.execute('select bool_xor(FALSE, TRUE) as res')1118assert [tuple(x) for x in self.cur] == [(1,)]11191120self.cur.execute('select bool_xor(FALSE, FALSE) as res')1121assert [tuple(x) for x in self.cur] == [(0,)]11221123desc = self.cur.description1124assert len(desc) == 11125assert desc[0].name == 'res'1126assert desc[0].type_code == ft.TINY1127assert desc[0].null_ok is False11281129def test_numpy_bool_and(self):1130"""Test vector bool AND using numpy arrays."""1131self.cur.execute(1132'select numpy_bool_and(bool_a, bool_b) as res '1133'from bool_data order by id',1134)11351136assert [tuple(x) for x in self.cur] == \1137[(0,), (0,), (0,), (1,)]11381139desc = self.cur.description1140assert len(desc) == 11141assert desc[0].name == 'res'1142assert desc[0].type_code == ft.TINY1143assert desc[0].null_ok is False11441145# NULL is not valid1146with self.assertRaises(self.conn.OperationalError):1147self.cur.execute(1148'select numpy_bool_and(bool_a, NULL) as res '1149'from bool_data order by id',1150)11511152def test_pandas_bool_and(self):1153"""Test vector bool AND using pandas Series."""1154self.cur.execute(1155'select pandas_bool_and(bool_a, bool_b) as res '1156'from bool_data order by id',1157)11581159assert [tuple(x) for x in self.cur] == \1160[(0,), (0,), (0,), (1,)]11611162desc = self.cur.description1163assert len(desc) == 11164assert desc[0].name == 'res'1165assert desc[0].type_code == ft.TINY1166assert desc[0].null_ok is False11671168# NULL is not valid1169with self.assertRaises(self.conn.OperationalError):1170self.cur.execute(1171'select pandas_bool_and(bool_a, NULL) as res '1172'from bool_data order by id',1173)11741175def test_polars_bool_and(self):1176"""Test vector bool AND using polars Series."""1177self.cur.execute(1178'select polars_bool_and(bool_a, bool_b) as res '1179'from bool_data order by id',1180)11811182assert [tuple(x) for x in self.cur] == \1183[(0,), (0,), (0,), (1,)]11841185desc = self.cur.description1186assert len(desc) == 11187assert desc[0].name == 'res'1188assert desc[0].type_code == ft.TINY1189assert desc[0].null_ok is False11901191# NULL is not valid1192with self.assertRaises(self.conn.OperationalError):1193self.cur.execute(1194'select polars_bool_and(bool_a, NULL) as res '1195'from bool_data order by id',1196)11971198def test_arrow_bool_and(self):1199"""Test vector bool AND using pyarrow arrays."""1200self.cur.execute(1201'select arrow_bool_and(bool_a, bool_b) as res '1202'from bool_data order by id',1203)12041205assert [tuple(x) for x in self.cur] == \1206[(0,), (0,), (0,), (1,)]12071208desc = self.cur.description1209assert len(desc) == 11210assert desc[0].name == 'res'1211assert desc[0].type_code == ft.TINY1212assert desc[0].null_ok is False12131214# NULL is not valid1215with self.assertRaises(self.conn.OperationalError):1216self.cur.execute(1217'select arrow_bool_and(bool_a, NULL) as res '1218'from bool_data order by id',1219)12201221def test_nullable_bool_and(self):1222"""Test nullable scalar bool AND."""1223self.cur.execute(1224'select nullable_bool_and(bool_a, bool_b) as res '1225'from bool_data_with_nulls order by id',1226)12271228assert [tuple(x) for x in self.cur] == \1229[(0,), (None,), (None,), (None,), (1,)]12301231desc = self.cur.description1232assert len(desc) == 11233assert desc[0].name == 'res'1234assert desc[0].type_code == ft.TINY1235assert desc[0].null_ok is True12361237self.cur.execute(1238'select nullable_bool_and(bool_a, NULL) as res '1239'from bool_data_with_nulls order by id',1240)12411242assert [tuple(x) for x in self.cur] == \1243[(None,), (None,), (None,), (None,), (None,)]12441245def test_numpy_nullable_bool_and(self):1246"""Test nullable vector bool AND using numpy."""1247self.cur.execute(1248'select numpy_nullable_bool_and(bool_a, bool_b) as res '1249'from bool_data_with_nulls order by id',1250)12511252# Note: Without masks, NULL values may behave like 0 in numpy1253assert [tuple(x) for x in self.cur] == \1254[(0,), (0,), (0,), (0,), (1,)]12551256desc = self.cur.description1257assert len(desc) == 11258assert desc[0].name == 'res'1259assert desc[0].type_code == ft.TINY1260assert desc[0].null_ok is True12611262def test_pandas_nullable_bool_and(self):1263"""Test nullable vector bool AND using pandas."""1264self.cur.execute(1265'select pandas_nullable_bool_and(bool_a, bool_b) as res '1266'from bool_data_with_nulls order by id',1267)12681269# Note: Without masks, NULL values may behave like 0 in pandas1270assert [tuple(x) for x in self.cur] == \1271[(0,), (0,), (0,), (0,), (1,)]12721273desc = self.cur.description1274assert len(desc) == 11275assert desc[0].name == 'res'1276assert desc[0].type_code == ft.TINY1277assert desc[0].null_ok is True12781279def test_polars_nullable_bool_and(self):1280"""Test nullable vector bool AND using polars."""1281self.cur.execute(1282'select polars_nullable_bool_and(bool_a, bool_b) as res '1283'from bool_data_with_nulls order by id',1284)12851286# Note: Without masks, NULL values may behave like 0 in polars1287assert [tuple(x) for x in self.cur] == \1288[(0,), (0,), (0,), (0,), (1,)]12891290desc = self.cur.description1291assert len(desc) == 11292assert desc[0].name == 'res'1293assert desc[0].type_code == ft.TINY1294assert desc[0].null_ok is True12951296def test_arrow_nullable_bool_and(self):1297"""Test nullable vector bool AND using pyarrow."""1298self.cur.execute(1299'select arrow_nullable_bool_and(bool_a, bool_b) as res '1300'from bool_data_with_nulls order by id',1301)13021303assert [tuple(x) for x in self.cur] == \1304[(0,), (None,), (None,), (None,), (1,)]13051306desc = self.cur.description1307assert len(desc) == 11308assert desc[0].name == 'res'1309assert desc[0].type_code == ft.TINY1310assert desc[0].null_ok is True13111312def test_numpy_nullable_bool_and_with_masks(self):1313"""Test nullable vector bool AND with masks using numpy."""1314self.cur.execute(1315'select numpy_nullable_bool_and_with_masks(bool_a, bool_b) as res '1316'from bool_data_with_nulls order by id',1317)13181319assert [tuple(x) for x in self.cur] == \1320[(0,), (None,), (None,), (None,), (1,)]13211322desc = self.cur.description1323assert len(desc) == 11324assert desc[0].name == 'res'1325assert desc[0].type_code == ft.TINY1326assert desc[0].null_ok is True13271328def test_pandas_nullable_bool_and_with_masks(self):1329"""Test nullable vector bool AND with masks using pandas."""1330self.cur.execute(1331'select pandas_nullable_bool_and_with_masks(bool_a, bool_b) as res '1332'from bool_data_with_nulls order by id',1333)13341335assert [tuple(x) for x in self.cur] == \1336[(0,), (None,), (None,), (None,), (1,)]13371338desc = self.cur.description1339assert len(desc) == 11340assert desc[0].name == 'res'1341assert desc[0].type_code == ft.TINY1342assert desc[0].null_ok is True13431344def test_polars_nullable_bool_and_with_masks(self):1345"""Test nullable vector bool AND with masks using polars."""1346self.cur.execute(1347'select polars_nullable_bool_and_with_masks(bool_a, bool_b) as res '1348'from bool_data_with_nulls order by id',1349)13501351assert [tuple(x) for x in self.cur] == \1352[(0,), (None,), (None,), (None,), (1,)]13531354desc = self.cur.description1355assert len(desc) == 11356assert desc[0].name == 'res'1357assert desc[0].type_code == ft.TINY1358assert desc[0].null_ok is True13591360def test_arrow_nullable_bool_and_with_masks(self):1361"""Test nullable vector bool AND with masks using pyarrow."""1362self.cur.execute(1363'select arrow_nullable_bool_and_with_masks(bool_a, bool_b) as res '1364'from bool_data_with_nulls order by id',1365)13661367assert [tuple(x) for x in self.cur] == \1368[(0,), (None,), (None,), (None,), (1,)]13691370desc = self.cur.description1371assert len(desc) == 11372assert desc[0].name == 'res'1373assert desc[0].type_code == ft.TINY1374assert desc[0].null_ok is True13751376# ========== END BOOL TESTS ==========13771378def test_string_mult(self):1379self.cur.execute(1380'select string_mult(name, value) as res '1381'from data order by id',1382)13831384assert [tuple(x) for x in self.cur] == [1385('antelopesantelopes',),1386('bearsbears',),1387('catscatscatscatscats',),1388('dogsdogsdogsdogs',),1389('',),1390]13911392desc = self.cur.description1393assert len(desc) == 11394assert desc[0].name == 'res'1395assert desc[0].type_code == ft.BLOB1396assert desc[0].null_ok is False13971398# NULL is not valid1399with self.assertRaises(self.conn.OperationalError):1400self.cur.execute(1401'select string_mult(NULL, value) as res '1402'from data order by id',1403)14041405def test_pandas_string_mult(self):1406self.cur.execute(1407'select pandas_string_mult(name, value) as res '1408'from data order by id',1409)14101411assert [tuple(x) for x in self.cur] == [1412('antelopesantelopes',),1413('bearsbears',),1414('catscatscatscatscats',),1415('dogsdogsdogsdogs',),1416('',),1417]14181419desc = self.cur.description1420assert len(desc) == 11421assert desc[0].name == 'res'1422assert desc[0].type_code == ft.BLOB1423assert desc[0].null_ok is False14241425# NULL is not valid1426with self.assertRaises(self.conn.OperationalError):1427self.cur.execute(1428'select pandas_string_mult(NULL, value) as res '1429'from data order by id',1430)14311432def test_numpy_string_mult(self):1433self.cur.execute(1434'select numpy_string_mult(name, value) as res '1435'from data order by id',1436)14371438assert [tuple(x) for x in self.cur] == [1439('antelopesantelopes',),1440('bearsbears',),1441('catscatscatscatscats',),1442('dogsdogsdogsdogs',),1443('',),1444]14451446desc = self.cur.description1447assert len(desc) == 11448assert desc[0].name == 'res'1449assert desc[0].type_code == ft.BLOB1450assert desc[0].null_ok is False14511452# NULL is not valid1453with self.assertRaises(self.conn.OperationalError):1454self.cur.execute(1455'select numpy_string_mult(NULL, value) as res '1456'from data order by id',1457)14581459def _test_polars_string_mult(self):1460self.cur.execute(1461'select polars_string_mult(name, value) as res '1462'from data order by id',1463)14641465assert [tuple(x) for x in self.cur] == [1466('antelopesantelopes',),1467('bearsbears',),1468('catscatscatscatscats',),1469('dogsdogsdogsdogs',),1470('',),1471]14721473desc = self.cur.description1474assert len(desc) == 11475assert desc[0].name == 'res'1476assert desc[0].type_code == ft.BLOB1477assert desc[0].null_ok is False14781479# NULL is not valid1480with self.assertRaises(self.conn.OperationalError):1481self.cur.execute(1482'select polars_string_mult(NULL, value) as res '1483'from data order by id',1484)14851486def _test_arrow_string_mult(self):1487self.cur.execute(1488'select arrow_string_mult(name, value) as res '1489'from data order by id',1490)14911492assert [tuple(x) for x in self.cur] == [1493('antelopesantelopes',),1494('bearsbears',),1495('catscatscatscatscats',),1496('dogsdogsdogsdogs',),1497('',),1498]14991500desc = self.cur.description1501assert len(desc) == 11502assert desc[0].name == 'res'1503assert desc[0].type_code == ft.BLOB1504assert desc[0].null_ok is False15051506# NULL is not valid1507with self.assertRaises(self.conn.OperationalError):1508self.cur.execute(1509'select arrow_string_mult(NULL, value) as res '1510'from data order by id',1511)15121513def test_nullable_string_mult(self):1514self.cur.execute(1515'select nullable_string_mult(name, value) as res '1516'from data_with_nulls order by id',1517)15181519assert [tuple(x) for x in self.cur] == [1520('antelopesantelopes',),1521(None,),1522(None,),1523(None,),1524('',),1525]15261527desc = self.cur.description1528assert len(desc) == 11529assert desc[0].name == 'res'1530assert desc[0].type_code == ft.BLOB1531assert desc[0].null_ok is True15321533def _test_varchar_mult(self):1534self.cur.execute(1535'select varchar_mult(name, value) as res '1536'from data order by id',1537)15381539assert [tuple(x) for x in self.cur] == [1540('antelopesantelopes',),1541('bearsbears',),1542('catscatscatscatscats',),1543('dogsdogsdogsdogs',),1544('',),1545]15461547desc = self.cur.description1548assert len(desc) == 11549assert desc[0].name == 'res'1550assert desc[0].type_code == ft.BLOB1551assert desc[0].null_ok is False15521553# NULL is not valid1554with self.assertRaises(self.conn.OperationalError):1555self.cur.execute(1556'select varchar_mult(NULL, value) as res '1557'from data order by id',1558)15591560def _test_nullable_varchar_mult(self):1561self.cur.execute(1562'select nullable_varchar_mult(name, value) as res '1563'from data_with_nulls order by id',1564)15651566assert [tuple(x) for x in self.cur] == [1567('antelopesantelopes',),1568(None,),1569(None,),1570(None,),1571('',),1572]15731574desc = self.cur.description1575assert len(desc) == 11576assert desc[0].name == 'res'1577assert desc[0].type_code == ft.BLOB1578assert desc[0].null_ok is True15791580def test_numpy_fixed_strings(self):1581self.cur.execute('select * from numpy_fixed_strings()')15821583assert [tuple(x) for x in self.cur] == [1584('hello',),1585('hi there 😜',),1586('😜 bye',),1587]15881589desc = self.cur.description1590assert len(desc) == 11591assert desc[0].name == 'res'1592assert desc[0].type_code == ft.BLOB1593assert desc[0].null_ok is False15941595def test_numpy_fixed_binary(self):1596self.cur.execute('select * from numpy_fixed_binary()')15971598assert [tuple(x) for x in self.cur] == [1599('hello'.encode('utf8'),),1600('hi there 😜'.encode('utf8'),),1601('😜 bye'.encode('utf8'),),1602]16031604desc = self.cur.description1605assert len(desc) == 11606assert desc[0].name == 'res'1607assert desc[0].type_code == ft.BLOB1608assert desc[0].null_ok is False16091610def test_no_args_no_return_value(self):1611self.cur.execute('select no_args_no_return_value() as res')16121613assert [tuple(x) for x in self.cur] == [(None,)]16141615desc = self.cur.description1616assert len(desc) == 11617assert desc[0].name == 'res'1618assert desc[0].type_code == ft.TINY1619assert desc[0].null_ok is True16201621def test_table_function(self):1622self.cur.execute('select * from table_function(5)')16231624assert [x[0] for x in self.cur] == [10, 10, 10, 10, 10]16251626desc = self.cur.description1627assert len(desc) == 11628assert desc[0].name == 'a'1629assert desc[0].type_code == ft.LONGLONG1630assert desc[0].null_ok is False16311632def test_async_table_function(self):1633self.cur.execute('select * from async_table_function(5)')16341635assert [x[0] for x in self.cur] == [10, 10, 10, 10, 10]16361637desc = self.cur.description1638assert len(desc) == 11639assert desc[0].name == 'a'1640assert desc[0].type_code == ft.LONGLONG1641assert desc[0].null_ok is False16421643def test_table_function_tuple(self):1644self.cur.execute('select * from table_function_tuple(3)')16451646out = list(self.cur)16471648assert out == [1649(10, 10.0, 'ten'),1650(10, 10.0, 'ten'),1651(10, 10.0, 'ten'),1652]16531654desc = self.cur.description1655assert len(desc) == 31656assert desc[0].name == 'c_int'1657assert desc[1].name == 'c_float'1658assert desc[2].name == 'c_str'16591660def test_table_function_struct(self):1661self.cur.execute('select * from table_function_struct(3)')16621663out = list(self.cur)16641665assert out == [1666(10, 10.0, 'ten'),1667(10, 10.0, 'ten'),1668(10, 10.0, 'ten'),1669]16701671desc = self.cur.description1672assert len(desc) == 31673assert desc[0].name == 'c_int'1674assert desc[1].name == 'c_float'1675assert desc[2].name == 'c_str'16761677def test_vec_function(self):1678self.cur.execute('select vec_function(5, 10) as res')16791680assert [tuple(x) for x in self.cur] == [(50.0,)]16811682def test_vec_function_ints(self):1683self.cur.execute('select vec_function_ints(5, 10) as res')16841685assert [tuple(x) for x in self.cur] == [(50,)]16861687def test_vec_function_df(self):1688self.cur.execute('select * from vec_function_df(5, 10)')16891690out = list(self.cur)16911692assert out == [1693(1, 1.1),1694(2, 2.2),1695(3, 3.3),1696]16971698desc = self.cur.description1699assert len(desc) == 21700assert desc[0].name == 'res'1701assert desc[0].type_code == ft.SHORT1702assert desc[0].null_ok is False1703assert desc[1].name == 'res2'1704assert desc[1].type_code == ft.DOUBLE1705assert desc[1].null_ok is False17061707def test_async_vec_function_df(self):1708self.cur.execute('select * from async_vec_function_df(5, 10)')17091710out = list(self.cur)17111712assert out == [1713(1, 1.1),1714(2, 2.2),1715(3, 3.3),1716]17171718desc = self.cur.description1719assert len(desc) == 21720assert desc[0].name == 'res'1721assert desc[0].type_code == ft.SHORT1722assert desc[0].null_ok is False1723assert desc[1].name == 'res2'1724assert desc[1].type_code == ft.DOUBLE1725assert desc[1].null_ok is False17261727def test_vec_function_ints_masked(self):1728self.cur.execute('select * from vec_function_ints_masked(5, 10)')17291730assert [tuple(x) for x in self.cur] == [(50,)]17311732desc = self.cur.description1733assert len(desc) == 11734assert desc[0].name == 'res'1735assert desc[0].type_code == ft.SHORT1736assert desc[0].null_ok is True17371738self.cur.execute('select * from vec_function_ints_masked(NULL, 10)')17391740assert [tuple(x) for x in self.cur] == [(None,)]17411742desc = self.cur.description1743assert len(desc) == 11744assert desc[0].name == 'res'1745assert desc[0].type_code == ft.SHORT1746assert desc[0].null_ok is True17471748self.cur.execute('select * from vec_function_ints_masked(5, NULL)')17491750assert [tuple(x) for x in self.cur] == [(None,)]17511752desc = self.cur.description1753assert len(desc) == 11754assert desc[0].name == 'res'1755assert desc[0].type_code == ft.SHORT1756assert desc[0].null_ok is True17571758def test_vec_function_ints_masked2(self):1759self.cur.execute('select * from vec_function_ints_masked2(5, 10)')17601761assert [tuple(x) for x in self.cur] == [(50, 50)]17621763desc = self.cur.description1764assert len(desc) == 21765assert desc[0].name == 'res'1766assert desc[0].type_code == ft.SHORT1767assert desc[0].null_ok is True1768assert desc[1].name == 'res2'1769assert desc[1].type_code == ft.SHORT1770assert desc[1].null_ok is True17711772self.cur.execute('select * from vec_function_ints_masked2(NULL, 10)')17731774assert [tuple(x) for x in self.cur] == [(None, None)]17751776desc = self.cur.description1777assert len(desc) == 21778assert desc[0].name == 'res'1779assert desc[0].type_code == ft.SHORT1780assert desc[0].null_ok is True1781assert desc[1].name == 'res2'1782assert desc[1].type_code == ft.SHORT1783assert desc[1].null_ok is True17841785self.cur.execute('select * from vec_function_ints_masked2(5, NULL)')17861787assert [tuple(x) for x in self.cur] == [(None, None)]17881789desc = self.cur.description1790assert len(desc) == 21791assert desc[0].name == 'res'1792assert desc[0].type_code == ft.SHORT1793assert desc[0].null_ok is True1794assert desc[1].name == 'res2'1795assert desc[1].type_code == ft.SHORT1796assert desc[1].null_ok is True179717981799