Skip to content

fix(sqla): preserve float precision when mixing int/float values in IN filters - #42752

Open
varadendrasimha511 wants to merge 4 commits into
apache:masterfrom
varadendrasimha511:fix-33206-numeric-filter-precision
Open

fix(sqla): preserve float precision when mixing int/float values in IN filters#42752
varadendrasimha511 wants to merge 4 commits into
apache:masterfrom
varadendrasimha511:fix-33206-numeric-filter-precision

Conversation

@varadendrasimha511

Copy link
Copy Markdown

SUMMARY

Fixes #33206.

When a numeric filter combines integer and decimal values (e.g. IN (33, 29.02)), SQLAlchemy may infer the bind parameter type from the first value in the list. If that first value is an int, subsequent float values in the same IN clause can be silently truncated during query compilation — dropping their decimal precision entirely and causing matching rows to disappear from results without any error.

This was reproduced and root-caused end-to-end:

  • Confirmed the frontend sends the correct, full-precision value (via browser DevTools network payload)
  • Confirmed the value arrives correct and untouched through cast_to_num/handle_single_value
  • Confirmed the column's SQLAlchemy type is correctly Float
  • Confirmed via debug logging that eq (the filter value list) is still fully correct immediately before sqla_col.in_(eq) is called
  • Isolated the exact trigger: only reproduces when an int and float are combined in the same IN list — decimals alone, or ints alone, both work correctly

The fix normalizes mixed int/float values to float right before binding, only when the target column is numeric and the list actually contains a mix — so purely-integer filters (e.g. ID lookups) are unaffected, addressing the earlier concern in #33230 about integer precision loss for large IDs.

BEFORE/AFTER

Before: Filtering global_sales IN (33, 29.02) on a table with both integer and decimal sales values silently drops the row matching 29.02, returning only the row matching 33.

After: Both rows are correctly returned.

TESTING INSTRUCTIONS

  1. Create a Table chart on any dataset with a float column containing both whole-number and decimal values (e.g. video_game_sales.global_sales)
  2. Add an "is in" filter with one integer-like value and one decimal value (e.g. 33 and 29.02)
  3. Before this fix: only the integer-matching row appears
  4. After this fix: both rows appear correctly

Added test_get_sqla_query_in_filter_preserves_float_precision in tests/unit_tests/models/helpers_test.py, which compiles the generated SQL and asserts the float value's precision is preserved in the IN clause.

ADDITIONAL INFORMATION

…N filters

Fixes apache#33206. When a numeric filter combines integer and float values (e.g. IN (33, 29.02)), SQLAlchemy may infer the bind parameter type from the first value in the list, silently truncating subsequent float values during query compilation. This normalizes mixed int/float values to float before binding when the target column is numeric, preventing silent data loss in filtered results.

Adds a regression test verifying the compiled SQL preserves decimal precision for mixed-type IN filters.
@dosubot dosubot Bot added the explore:filter Related to filters in Explore label Aug 4, 2026
@bito-code-review

bito-code-review Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #0000d9

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset/models/helpers.py - 1
    • Import style inconsistency · Line 4103-4103
      Use the direct `GenericDataType` import (already at line 130) instead of the `utils.GenericDataType` prefix for consistency with line 4044 and other references in this function.
Review Details
  • Files reviewed - 2 · Commit Range: ee9a84e..ee9a84e
    • superset/models/helpers.py
    • tests/unit_tests/models/helpers_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@netlify

netlify Bot commented Aug 4, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 062e9c0
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a719add8d2b040008266c2b
😎 Deploy Preview https://deploy-preview-42752--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Comment on lines +4106 to +4109
eq = [
float(v) if isinstance(v, (int, float)) else v
for v in eq
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Converting every integer in a mixed list with float(v) loses precision for integers larger than the exact range of IEEE-754 doubles. For example, 9007199254740993 becomes 9007199254740992.0, so an integer-column filter can match the wrong row or fail to match the requested ID. Preserve integer values and only force the bind type to a floating type without changing their numeric representation. [type error]

Severity Level: Major ⚠️
- ❌ BigInteger `IN` filters can lose exact integer values.
- ⚠️ Chart query results can omit or misidentify matching rows.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/models/helpers.py
**Line:** 4106:4109
**Comment:**
	*Type Error: Converting every integer in a mixed list with `float(v)` loses precision for integers larger than the exact range of IEEE-754 doubles. For example, `9007199254740993` becomes `9007199254740992.0`, so an integer-column filter can match the wrong row or fail to match the requested ID. Preserve integer values and only force the bind type to a floating type without changing their numeric representation.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed—mixing 9007199254740993 with a fractional value changes the requested integer to 9007199254740992.0, so exact NUMERIC filters can match the wrong row. Could we avoid coercing the original integers to binary float?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed—the current conversion can corrupt integers above 2**53, so this should not be fixed by converting the original values to float.

A safer approach is to avoid mixing bind types altogether: keep integer values in one IN predicate and floating-point values in another, then combine them with OR:

if (
    target_generic_type == utils.GenericDataType.NUMERIC
    and any(type(v) is float for v in eq)
    and any(type(v) is int for v in eq)
):
    integer_values = [v for v in eq if type(v) is int]
    float_values = [v for v in eq if type(v) is float]

    cond = or_(
        sqla_col.in_(integer_values),
        sqla_col.in_(float_values),
    )
else:
    cond = sqla_col.in_(eq)

This preserves values such as 9007199254740993 as integers while ensuring the decimal values are bound as floats. The regression test should also include a large integer mixed with a fractional value to verify that exact integer precision is retained.

Comment on lines +2721 to +2724
sqla_query.sqla_query.compile(
dialect=engine.dialect,
compile_kwargs={"literal_binds": True},
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Using literal_binds=True removes the bind-parameter path that caused the production regression, because SQLAlchemy renders the Python values directly into the SQL text. This assertion can pass even when execution with bound parameters still infers an integer type from the first value and truncates the decimal. Assert the generated bind parameter types/values or execute the query against an engine that reproduces the affected behavior. [incomplete implementation]

Severity Level: Major ⚠️
- ⚠️ Regression test does not cover bound-parameter execution.
- ⚠️ Precision truncation could regress undetected in numeric IN filters.
- ⚠️ Query correctness remains unverified for affected database dialects.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/unit_tests/models/helpers_test.py
**Line:** 2721:2724
**Comment:**
	*Incomplete Implementation: Using `literal_binds=True` removes the bind-parameter path that caused the production regression, because SQLAlchemy renders the Python values directly into the SQL text. This assertion can pass even when execution with bound parameters still infers an integer type from the first value and truncates the decimal. Assert the generated bind parameter types/values or execute the query against an engine that reproduces the affected behavior.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.59%. Comparing base (1666cca) to head (153db5b).
⚠️ Report is 13 commits behind head on master.

Files with missing lines Patch % Lines
superset/models/helpers.py 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #42752      +/-   ##
==========================================
- Coverage   65.59%   65.59%   -0.01%     
==========================================
  Files        2819     2819              
  Lines      160166   160168       +2     
  Branches    36569    36570       +1     
==========================================
- Hits       105065   105056       -9     
- Misses      53053    53062       +9     
- Partials     2048     2050       +2     
Flag Coverage Δ
hive 38.09% <0.00%> (-0.01%) ⬇️
mysql 57.92% <0.00%> (-0.01%) ⬇️
postgres 57.96% <0.00%> (-0.01%) ⬇️
presto 40.02% <0.00%> (-0.01%) ⬇️
python 59.34% <0.00%> (-0.01%) ⬇️
sqlite 57.59% <0.00%> (-0.01%) ⬇️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bito-code-review

bito-code-review Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #c52c31

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: ee9a84e..4675ab9
    • tests/unit_tests/models/helpers_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #340247

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 4675ab9..153db5b
    • tests/unit_tests/models/helpers_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

# 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(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This normalization only runs in the no-NULL branch, so IN [33, 29.02, NULL] still compiles the fractional value as 29 and omits matching rows. Could we hoist the normalization before eq_without_none is derived and cover the NULL-bearing list?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

explore:filter Related to filters in Explore size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect filtering for numeric values with different decimal precision

2 participants