You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/08/10 18:12:51 UTC
(superset) branch master updated: fix(sqla): preserve float precision when mixing int/float values in IN filters (#42752)
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new b98e1571043 fix(sqla): preserve float precision when mixing int/float values in IN filters (#42752)
b98e1571043 is described below
commit b98e1571043aa1af4c2aab0e6e0de401fae525f6
Author: varadendrasimha511 <10...@users.noreply.github.com>
AuthorDate: Mon Aug 10 23:42:43 2026 +0530
fix(sqla): preserve float precision when mixing int/float values in IN filters (#42752)
---
superset/models/helpers.py | 13 +++++
tests/unit_tests/models/helpers_test.py | 100 ++++++++++++++++++++++++++++++++
2 files changed, 113 insertions(+)
diff --git a/superset/models/helpers.py b/superset/models/helpers.py
index 671c07f3026..aeb9e79a21a 100644
--- a/superset/models/helpers.py
+++ b/superset/models/helpers.py
@@ -4308,6 +4308,18 @@ class ExploreMixin: # pylint: disable=too-many-public-methods
raise QueryObjectValidationError(
_("Filter value list cannot be empty")
)
+
+ # Normalize mixed int/float values before binding, since
+ # SQLAlchemy may infer the bind parameter type from the
+ # first element and silently truncate other values
+ # (see #33206)
+ if target_generic_type == utils.GenericDataType.NUMERIC and any(
+ isinstance(v, float) for v in eq
+ ):
+ eq = [
+ float(v) if isinstance(v, (int, float)) else v for v in eq
+ ]
+
if len(eq) > len(
eq_without_none := [x for x in eq if x is not None]
):
@@ -4318,6 +4330,7 @@ class ExploreMixin: # pylint: disable=too-many-public-methods
cond = is_null_cond
else:
cond = sqla_col.in_(eq)
+
if op == utils.FilterOperator.NOT_IN:
cond = ~cond
target_clause_list.append(cond)
diff --git a/tests/unit_tests/models/helpers_test.py b/tests/unit_tests/models/helpers_test.py
index 49f122b60d2..a7156b795b9 100644
--- a/tests/unit_tests/models/helpers_test.py
+++ b/tests/unit_tests/models/helpers_test.py
@@ -2754,6 +2754,106 @@ def test_calculated_column_non_boolean_filter_is_parenthesized(
)
+def test_get_sqla_query_in_filter_preserves_float_precision(
+ database: Database,
+) -> None:
+ """
+ Test that mixing integer and float values in an "IN" filter does not
+ truncate the float value's decimal precision when the compiled query
+ binds parameters (see #33206).
+ """
+ from superset.connectors.sqla.models import SqlaTable, TableColumn
+
+ table = SqlaTable(
+ database=database,
+ schema=None,
+ table_name="t",
+ columns=[
+ TableColumn(column_name="global_sales", type="FLOAT"),
+ ],
+ )
+
+ sqla_query = table.get_sqla_query(
+ columns=["global_sales"],
+ filter=[
+ {
+ "col": "global_sales",
+ "op": "IN",
+ "val": [33, 29.02],
+ },
+ ],
+ extras={},
+ is_timeseries=False,
+ metrics=[],
+ )
+
+ with database.get_sqla_engine() as engine:
+ sql = str(
+ sqla_query.sqla_query.compile(
+ dialect=engine.dialect,
+ compile_kwargs={"literal_binds": True},
+ )
+ )
+
+ # The decimal value must retain its precision in the compiled SQL,
+ # rather than being silently truncated to the integer 29.
+ assert "29.02" in sql, (
+ f"Expected float value 29.02 to be preserved in the IN clause, "
+ f"but it was likely truncated. Generated SQL: {sql}"
+ )
+
+
+def test_get_sqla_query_in_filter_preserves_float_precision_with_null(
+ database: Database,
+) -> None:
+ """
+ Test that mixing integer and float values with a NULL in an "IN" filter
+ does not truncate the float value's decimal precision, even though this
+ triggers the separate `eq_without_none` code path (see #33206).
+ """
+ from superset.connectors.sqla.models import SqlaTable, TableColumn
+
+ table = SqlaTable(
+ database=database,
+ schema=None,
+ table_name="t",
+ columns=[
+ TableColumn(column_name="global_sales", type="FLOAT"),
+ ],
+ )
+
+ sqla_query = table.get_sqla_query(
+ columns=["global_sales"],
+ filter=[
+ {
+ "col": "global_sales",
+ "op": "IN",
+ "val": [33, 29.02, None], # type: ignore[list-item]
+ },
+ ],
+ extras={},
+ is_timeseries=False,
+ metrics=[],
+ )
+
+ with database.get_sqla_engine() as engine:
+ sql = str(
+ sqla_query.sqla_query.compile(
+ dialect=engine.dialect,
+ compile_kwargs={"literal_binds": True},
+ )
+ )
+
+ # The decimal value must retain its precision in the compiled SQL,
+ # rather than being silently truncated to the integer 29, even when
+ # the filter list also contains a NULL value.
+ assert "29.02" in sql, (
+ f"Expected float value 29.02 to be preserved in the IN clause "
+ f"even with a NULL present, but it was likely truncated. "
+ f"Generated SQL: {sql}"
+ )
+
+
def test_multiple_calculated_columns_each_parenthesized(
database: Database,
) -> None: