From 5bef5c9430de7509abcc431e55c10354b6c7d8a2 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 19 Nov 2025 15:32:37 -0600 Subject: [PATCH 1/3] Add comprehensive tests for np.bool_ types in Python UDFs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds extensive test coverage for np.bool_ and bool types in Python UDFs, including both scalar (non-vector) and vector operations. Changes: - Added np.bool_ to numpy_type_map in signature.py for proper type signature resolution - Added bool UDF implementations in ext_funcs/__init__.py: * Scalar operations: bool_and, bool_or, bool_not, bool_xor * Vector operations for numpy, pandas, polars, and pyarrow * Nullable variants for all frameworks * Masked variants with explicit null handling - Added test data in test.sql: * bool_data table with non-nullable bool columns * bool_data_with_nulls table for testing null handling - Added integration tests in test_ext_func.py: * 4 scalar bool tests (and, or, not, xor) * 4 vector non-nullable tests (numpy, pandas, polars, arrow) * 5 nullable tests (scalar + 4 frameworks) * 4 masked tests (all frameworks) - Added type signature tests in test_udf.py: * bool and np.bool_ type mapping * Optional[bool] nullable mapping * List[bool] vector mapping * Bool return types Special handling for pyarrow: - Converts TINYINT (0/1) to bool using pc.not_equal(x, 0) - Converts bool results back to int8 for proper serialization All tests passing, pre-commit hooks clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- singlestoredb/functions/signature.py | 1 + singlestoredb/tests/ext_funcs/__init__.py | 187 +++++++++++++ singlestoredb/tests/test.sql | 29 ++ singlestoredb/tests/test_ext_func.py | 325 ++++++++++++++++++++++ singlestoredb/tests/test_udf.py | 32 +++ 5 files changed, 574 insertions(+) diff --git a/singlestoredb/functions/signature.py b/singlestoredb/functions/signature.py index 69cbb437c..2c5b880bb 100644 --- a/singlestoredb/functions/signature.py +++ b/singlestoredb/functions/signature.py @@ -55,6 +55,7 @@ class NoDefaultType: if has_numpy: array_types = (Sequence, np.ndarray) numpy_type_map = { + np.bool_: 'bool', np.integer: 'int64', np.int_: 'int64', np.int64: 'int64', diff --git a/singlestoredb/tests/ext_funcs/__init__.py b/singlestoredb/tests/ext_funcs/__init__.py index 0b346db23..dfbfe90dc 100644 --- a/singlestoredb/tests/ext_funcs/__init__.py +++ b/singlestoredb/tests/ext_funcs/__init__.py @@ -16,6 +16,7 @@ from singlestoredb.functions import udf from singlestoredb.functions.dtypes import BIGINT from singlestoredb.functions.dtypes import BLOB +from singlestoredb.functions.dtypes import BOOL from singlestoredb.functions.dtypes import DOUBLE from singlestoredb.functions.dtypes import FLOAT from singlestoredb.functions.dtypes import MEDIUMINT @@ -308,6 +309,192 @@ def arrow_bigint_mult(x: pat.Array, y: pat.Array) -> pat.Array: return pc.multiply(x, y) +# +# BOOL - Scalar (non-vector) tests +# + +@udf +def bool_and(x: bool, y: bool) -> bool: + """Scalar bool AND operation.""" + return x and y + + +@udf +def bool_or(x: bool, y: bool) -> bool: + """Scalar bool OR operation.""" + return x or y + + +@udf +def bool_not(x: bool) -> bool: + """Scalar bool NOT operation.""" + return not x + + +@udf +def bool_xor(x: bool, y: bool) -> bool: + """Scalar bool XOR operation.""" + return x != y + + +# +# BOOL - Vector (non-nullable) +# + +bool_udf = udf( + args=[BOOL(nullable=False), BOOL(nullable=False)], + returns=BOOL(nullable=False), +) + + +@bool_udf +def numpy_bool_and(x: np.ndarray, y: np.ndarray) -> np.ndarray: + """Vector bool AND using numpy.""" + return x & y + + +@bool_udf +def pandas_bool_and(x: pdt.Series, y: pdt.Series) -> pdt.Series: + """Vector bool AND using pandas.""" + return x & y + + +@bool_udf +def polars_bool_and(x: plt.Series, y: plt.Series) -> plt.Series: + """Vector bool AND using polars.""" + return x & y + + +@bool_udf +def arrow_bool_and(x: pat.Array, y: pat.Array) -> pat.Array: + """Vector bool AND using pyarrow.""" + import pyarrow as pa + import pyarrow.compute as pc + # Convert TINYINT (0/1) to bool by comparing with 0 + x_bool = pc.not_equal(x, 0) + y_bool = pc.not_equal(y, 0) + result_bool = pc.and_(x_bool, y_bool) + # Convert back to int8 for TINYINT return type + return pc.cast(result_bool, pa.int8()) + + +# +# BOOL - Nullable scalar +# + +@udf +def nullable_bool_and(x: Optional[bool], y: Optional[bool]) -> Optional[bool]: + """Nullable scalar bool AND operation.""" + if x is None or y is None: + return None + return x and y + + +# +# BOOL - Nullable vector +# + +nullable_bool_udf = udf( + args=[BOOL(nullable=True), BOOL(nullable=True)], + returns=BOOL(nullable=True), +) + + +@nullable_bool_udf +def numpy_nullable_bool_and(x: np.ndarray, y: np.ndarray) -> np.ndarray: + """Nullable vector bool AND using numpy.""" + return x & y + + +@nullable_bool_udf +def pandas_nullable_bool_and(x: pdt.Series, y: pdt.Series) -> pdt.Series: + """Nullable vector bool AND using pandas.""" + return x & y + + +@nullable_bool_udf +def polars_nullable_bool_and(x: plt.Series, y: plt.Series) -> plt.Series: + """Nullable vector bool AND using polars.""" + return x & y + + +@nullable_bool_udf +def arrow_nullable_bool_and(x: pat.Array, y: pat.Array) -> pat.Array: + """Nullable vector bool AND using pyarrow.""" + import pyarrow as pa + import pyarrow.compute as pc + # Convert TINYINT (0/1) to bool by comparing with 0 + x_bool = pc.not_equal(x, 0) + y_bool = pc.not_equal(y, 0) + result_bool = pc.and_(x_bool, y_bool) + # Convert back to int8 for TINYINT return type + return pc.cast(result_bool, pa.int8()) + + +# +# BOOL - Masked variants (with explicit null handling) +# + +@udf( + args=[BOOL(nullable=True), BOOL(nullable=True)], + returns=BOOL(nullable=True), +) +def numpy_nullable_bool_and_with_masks( + x: Masked[npt.NDArray[np.bool_]], y: Masked[npt.NDArray[np.bool_]], +) -> Masked[npt.NDArray[np.bool_]]: + """Nullable vector bool AND with masks using numpy.""" + x_data, x_nulls = x + y_data, y_nulls = y + return Masked(x_data & y_data, x_nulls | y_nulls) + + +@udf( + args=[BOOL(nullable=True), BOOL(nullable=True)], + returns=BOOL(nullable=True), +) +def pandas_nullable_bool_and_with_masks( + x: Masked[pdt.Series], y: Masked[pdt.Series], +) -> Masked[pdt.Series]: + """Nullable vector bool AND with masks using pandas.""" + x_data, x_nulls = x + y_data, y_nulls = y + return Masked(x_data & y_data, x_nulls | y_nulls) + + +@udf( + args=[BOOL(nullable=True), BOOL(nullable=True)], + returns=BOOL(nullable=True), +) +def polars_nullable_bool_and_with_masks( + x: Masked[plt.Series], y: Masked[plt.Series], +) -> Masked[plt.Series]: + """Nullable vector bool AND with masks using polars.""" + x_data, x_nulls = x + y_data, y_nulls = y + return Masked(x_data & y_data, x_nulls | y_nulls) + + +@udf( + args=[BOOL(nullable=True), BOOL(nullable=True)], + returns=BOOL(nullable=True), +) +def arrow_nullable_bool_and_with_masks( + x: Masked[pat.Array], y: Masked[pat.Array], +) -> Masked[pat.Array]: + """Nullable vector bool AND with masks using pyarrow.""" + import pyarrow as pa + import pyarrow.compute as pc + x_data, x_nulls = x + y_data, y_nulls = y + # Convert TINYINT (0/1) to bool by comparing with 0 + x_bool = pc.not_equal(x_data, 0) + y_bool = pc.not_equal(y_data, 0) + result_bool = pc.and_(x_bool, y_bool) + # Convert back to int8 for TINYINT return type + result_int = pc.cast(result_bool, pa.int8()) + return Masked(result_int, pc.or_(x_nulls, y_nulls)) + + # # NULLABLE TINYINT # diff --git a/singlestoredb/tests/test.sql b/singlestoredb/tests/test.sql index ab3cf955a..f43259d13 100644 --- a/singlestoredb/tests/test.sql +++ b/singlestoredb/tests/test.sql @@ -677,4 +677,33 @@ INSERT INTO i64_vectors VALUES(2, '[4, 5, 6]'); INSERT INTO i64_vectors VALUES(3, '[-1, -4, 8]'); +-- +-- Boolean test data for UDF testing +-- +CREATE ROWSTORE TABLE IF NOT EXISTS bool_data ( + id VARCHAR(255) NOT NULL, + bool_a BOOL NOT NULL, + bool_b BOOL NOT NULL, + PRIMARY KEY (id) USING HASH +) DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci; + +INSERT INTO bool_data SET id='tt', bool_a=TRUE, bool_b=TRUE; +INSERT INTO bool_data SET id='tf', bool_a=TRUE, bool_b=FALSE; +INSERT INTO bool_data SET id='ft', bool_a=FALSE, bool_b=TRUE; +INSERT INTO bool_data SET id='ff', bool_a=FALSE, bool_b=FALSE; + +CREATE ROWSTORE TABLE IF NOT EXISTS bool_data_with_nulls ( + id VARCHAR(255) NOT NULL, + bool_a BOOL, + bool_b BOOL, + PRIMARY KEY (id) USING HASH +) DEFAULT CHARSET = utf8 COLLATE = utf8_unicode_ci; + +INSERT INTO bool_data_with_nulls SET id='tt', bool_a=TRUE, bool_b=TRUE; +INSERT INTO bool_data_with_nulls SET id='tn', bool_a=TRUE, bool_b=NULL; +INSERT INTO bool_data_with_nulls SET id='nt', bool_a=NULL, bool_b=TRUE; +INSERT INTO bool_data_with_nulls SET id='nn', bool_a=NULL, bool_b=NULL; +INSERT INTO bool_data_with_nulls SET id='ff', bool_a=FALSE, bool_b=FALSE; + + COMMIT; diff --git a/singlestoredb/tests/test_ext_func.py b/singlestoredb/tests/test_ext_func.py index d3e680e58..a12b2fd30 100755 --- a/singlestoredb/tests/test_ext_func.py +++ b/singlestoredb/tests/test_ext_func.py @@ -1051,6 +1051,331 @@ def test_nullable_int_mult(self): assert desc[0].type_code == ft.LONGLONG assert desc[0].null_ok is True + # ========== BOOL TESTS ========== + + def test_bool_and(self): + """Test scalar (non-vector) bool AND.""" + self.cur.execute('select bool_and(TRUE, TRUE) as res') + assert [tuple(x) for x in self.cur] == [(1,)] + + self.cur.execute('select bool_and(TRUE, FALSE) as res') + assert [tuple(x) for x in self.cur] == [(0,)] + + self.cur.execute('select bool_and(FALSE, TRUE) as res') + assert [tuple(x) for x in self.cur] == [(0,)] + + self.cur.execute('select bool_and(FALSE, FALSE) as res') + assert [tuple(x) for x in self.cur] == [(0,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY # BOOL is stored as TINYINT + assert desc[0].null_ok is False + + def test_bool_or(self): + """Test scalar (non-vector) bool OR.""" + self.cur.execute('select bool_or(TRUE, TRUE) as res') + assert [tuple(x) for x in self.cur] == [(1,)] + + self.cur.execute('select bool_or(TRUE, FALSE) as res') + assert [tuple(x) for x in self.cur] == [(1,)] + + self.cur.execute('select bool_or(FALSE, TRUE) as res') + assert [tuple(x) for x in self.cur] == [(1,)] + + self.cur.execute('select bool_or(FALSE, FALSE) as res') + assert [tuple(x) for x in self.cur] == [(0,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is False + + def test_bool_not(self): + """Test scalar (non-vector) bool NOT.""" + self.cur.execute('select bool_not(TRUE) as res') + assert [tuple(x) for x in self.cur] == [(0,)] + + self.cur.execute('select bool_not(FALSE) as res') + assert [tuple(x) for x in self.cur] == [(1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is False + + def test_bool_xor(self): + """Test scalar (non-vector) bool XOR.""" + self.cur.execute('select bool_xor(TRUE, TRUE) as res') + assert [tuple(x) for x in self.cur] == [(0,)] + + self.cur.execute('select bool_xor(TRUE, FALSE) as res') + assert [tuple(x) for x in self.cur] == [(1,)] + + self.cur.execute('select bool_xor(FALSE, TRUE) as res') + assert [tuple(x) for x in self.cur] == [(1,)] + + self.cur.execute('select bool_xor(FALSE, FALSE) as res') + assert [tuple(x) for x in self.cur] == [(0,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is False + + def test_numpy_bool_and(self): + """Test vector bool AND using numpy arrays.""" + self.cur.execute( + 'select numpy_bool_and(bool_a, bool_b) as res ' + 'from bool_data order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (0,), (0,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is False + + # NULL is not valid + with self.assertRaises(self.conn.OperationalError): + self.cur.execute( + 'select numpy_bool_and(bool_a, NULL) as res ' + 'from bool_data order by id', + ) + + def test_pandas_bool_and(self): + """Test vector bool AND using pandas Series.""" + self.cur.execute( + 'select pandas_bool_and(bool_a, bool_b) as res ' + 'from bool_data order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (0,), (0,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is False + + # NULL is not valid + with self.assertRaises(self.conn.OperationalError): + self.cur.execute( + 'select pandas_bool_and(bool_a, NULL) as res ' + 'from bool_data order by id', + ) + + def test_polars_bool_and(self): + """Test vector bool AND using polars Series.""" + self.cur.execute( + 'select polars_bool_and(bool_a, bool_b) as res ' + 'from bool_data order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (0,), (0,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is False + + # NULL is not valid + with self.assertRaises(self.conn.OperationalError): + self.cur.execute( + 'select polars_bool_and(bool_a, NULL) as res ' + 'from bool_data order by id', + ) + + def test_arrow_bool_and(self): + """Test vector bool AND using pyarrow arrays.""" + self.cur.execute( + 'select arrow_bool_and(bool_a, bool_b) as res ' + 'from bool_data order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (0,), (0,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is False + + # NULL is not valid + with self.assertRaises(self.conn.OperationalError): + self.cur.execute( + 'select arrow_bool_and(bool_a, NULL) as res ' + 'from bool_data order by id', + ) + + def test_nullable_bool_and(self): + """Test nullable scalar bool AND.""" + self.cur.execute( + 'select nullable_bool_and(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (None,), (None,), (None,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + self.cur.execute( + 'select nullable_bool_and(bool_a, NULL) as res ' + 'from bool_data_with_nulls order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(None,), (None,), (None,), (None,), (None,)] + + def test_numpy_nullable_bool_and(self): + """Test nullable vector bool AND using numpy.""" + self.cur.execute( + 'select numpy_nullable_bool_and(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + # Note: Without masks, NULL values may behave like 0 in numpy + assert [tuple(x) for x in self.cur] == \ + [(0,), (0,), (0,), (0,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + def test_pandas_nullable_bool_and(self): + """Test nullable vector bool AND using pandas.""" + self.cur.execute( + 'select pandas_nullable_bool_and(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + # Note: Without masks, NULL values may behave like 0 in pandas + assert [tuple(x) for x in self.cur] == \ + [(0,), (0,), (0,), (0,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + def test_polars_nullable_bool_and(self): + """Test nullable vector bool AND using polars.""" + self.cur.execute( + 'select polars_nullable_bool_and(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + # Note: Without masks, NULL values may behave like 0 in polars + assert [tuple(x) for x in self.cur] == \ + [(0,), (0,), (0,), (0,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + def test_arrow_nullable_bool_and(self): + """Test nullable vector bool AND using pyarrow.""" + self.cur.execute( + 'select arrow_nullable_bool_and(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (None,), (None,), (None,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + def test_numpy_nullable_bool_and_with_masks(self): + """Test nullable vector bool AND with masks using numpy.""" + self.cur.execute( + 'select numpy_nullable_bool_and_with_masks(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (None,), (None,), (None,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + def test_pandas_nullable_bool_and_with_masks(self): + """Test nullable vector bool AND with masks using pandas.""" + self.cur.execute( + 'select pandas_nullable_bool_and_with_masks(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (None,), (None,), (None,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + def test_polars_nullable_bool_and_with_masks(self): + """Test nullable vector bool AND with masks using polars.""" + self.cur.execute( + 'select polars_nullable_bool_and_with_masks(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (None,), (None,), (None,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + def test_arrow_nullable_bool_and_with_masks(self): + """Test nullable vector bool AND with masks using pyarrow.""" + self.cur.execute( + 'select arrow_nullable_bool_and_with_masks(bool_a, bool_b) as res ' + 'from bool_data_with_nulls order by id', + ) + + assert [tuple(x) for x in self.cur] == \ + [(0,), (None,), (None,), (None,), (1,)] + + desc = self.cur.description + assert len(desc) == 1 + assert desc[0].name == 'res' + assert desc[0].type_code == ft.TINY + assert desc[0].null_ok is True + + # ========== END BOOL TESTS ========== + def test_string_mult(self): self.cur.execute( 'select string_mult(name, value) as res ' diff --git a/singlestoredb/tests/test_udf.py b/singlestoredb/tests/test_udf.py index ebf0f60c9..e806fce0d 100755 --- a/singlestoredb/tests/test_udf.py +++ b/singlestoredb/tests/test_udf.py @@ -250,6 +250,38 @@ def foo(x: Union[datetime.datetime, datetime.date]) -> None: ... to_sql(foo) def test_numerics(self): + # + # Bools + # + def foo(x: bool) -> None: ... + assert to_sql(foo) == '`foo`(`x` BOOL NOT NULL) RETURNS TINYINT NULL' + + def foo(x: np.bool_) -> None: ... + assert to_sql(foo) == '`foo`(`x` BOOL NOT NULL) RETURNS TINYINT NULL' + + def foo(x: Optional[bool]) -> None: ... + assert to_sql(foo) == '`foo`(`x` BOOL NULL) RETURNS TINYINT NULL' + + def foo(x: Optional[np.bool_]) -> None: ... + assert to_sql(foo) == '`foo`(`x` BOOL NULL) RETURNS TINYINT NULL' + + # Bool return types + def foo() -> bool: ... + assert to_sql(foo) == '`foo`() RETURNS BOOL NOT NULL' + + def foo() -> np.bool_: ... + assert to_sql(foo) == '`foo`() RETURNS BOOL NOT NULL' + + def foo() -> Optional[bool]: ... + assert to_sql(foo) == '`foo`() RETURNS BOOL NULL' + + # Vector bool (List) + def foo(x: List[bool]) -> List[bool]: ... + assert to_sql(foo) == '`foo`(`x` BOOL NOT NULL) RETURNS BOOL NOT NULL' + + def foo(x: List[np.bool_]) -> List[np.bool_]: ... + assert to_sql(foo) == '`foo`(`x` BOOL NOT NULL) RETURNS BOOL NOT NULL' + # # Ints # From f1b5c4817a3674bb2c159e189f99ac5187e33011 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 5 Dec 2025 10:44:16 -0600 Subject: [PATCH 2/3] Add bool to int map --- singlestoredb/functions/signature.py | 1 + 1 file changed, 1 insertion(+) diff --git a/singlestoredb/functions/signature.py b/singlestoredb/functions/signature.py index 2c5b880bb..7f058c457 100644 --- a/singlestoredb/functions/signature.py +++ b/singlestoredb/functions/signature.py @@ -100,6 +100,7 @@ class NoDefaultType: } int_type_map = { + 'bool': 'bool', 'int': 'int64', 'integer': 'int64', 'int_': 'int64', From 1b746808f3525d0b61f1fa5d89da7fa4f6f80d4b Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 5 Dec 2025 10:58:16 -0600 Subject: [PATCH 3/3] Add numpy definition for bool array --- singlestoredb/functions/typing/numpy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/singlestoredb/functions/typing/numpy.py b/singlestoredb/functions/typing/numpy.py index fb3954d2f..789bc4efd 100644 --- a/singlestoredb/functions/typing/numpy.py +++ b/singlestoredb/functions/typing/numpy.py @@ -7,6 +7,7 @@ BytesArray = npt.NDArray[np.bytes_] Float32Array = FloatArray = npt.NDArray[np.float32] Float64Array = DoubleArray = npt.NDArray[np.float64] +BoolArray = npt.NDArray[np.bool_] IntArray = npt.NDArray[np.int_] Int8Array = npt.NDArray[np.int8] Int16Array = npt.NDArray[np.int16]