Skip to content

fix: keep -0.0 rows in TopK dynamic filter when the threshold is a float zero - #26718

Open
LiuYC3213687107 wants to merge 4 commits into
apache:mainfrom
LiuYC3213687107:fix-topk-negative-zero
Open

LiuYC3213687107 wants to merge 4 commits into
apache:mainfrom
LiuYC3213687107:fix-topk-negative-zero

Conversation

@LiuYC3213687107

Copy link
Copy Markdown

Which issue does this PR close?

Rationale for this change

ORDER BY <double>, ... LIMIT n can drop -0.0 rows that belong in the result when the input arrives in more than one batch.

The TopK heap orders -0.0 before 0.0 (IEEE 754 totalOrder, same as the full sort), but since #22835 comparisons treat -0.0 = 0.0. With a heap boundary of (0.0, 1) the dynamic filter becomes x < 0.0 OR (x = 0.0 AND y < 1), which rejects (-0.0, 5) even though the heap would rank it ahead of the boundary.

What changes are included in this PR?

In TopK::build_filter_expression, when the threshold of a sort key is a floating-point zero (either sign):

  • use a non-strict bound for that key (x <= 0.0 for ASC, x >= 0.0 for DESC), so both zeros pass the filter, and
  • stop adding predicates for later sort keys, since the equality on this key no longer identifies the heap's tie group.

The dynamic filter is only a pruning hint, so making it more conservative for this one value cannot change results; the heap still decides the final order. This mirrors how the function already stops at the first nested-type key.

What is the testing strategy for this PR?

Added sqllogictest cases to topk.slt that reproduce the issue with two batches, for both ASC and DESC. Without the fix the LIMIT 2 queries return (0, 7), (0, 1) instead of (-0, 5), (-0, 7). (sqllogictest prints -0.0 as 0, so the y column distinguishes the rows.)

Are there any user-facing changes?

No API changes. ORDER BY ... LIMIT queries over float columns containing both 0.0 and -0.0 now return correct results.

AI assistance

This change was prepared with Claude Code (Anthropic): the fix in datafusion/physical-plan/src/topk/mod.rs, the tests in topk.slt, and this description. I reviewed and verified the change myself.

🤖 Generated with Claude Code

@github-actions github-actions Bot added sqllogictest SQL Logic Tests (.slt) physical-plan Changes to the physical-plan crate labels Oct 24, 2026
…oat zero

The TopK heap orders -0.0 before 0.0, but comparisons treat them as equal
since apache#22835, so a filter like `x < 0.0 OR (x = 0.0 AND y < 1)` dropped
-0.0 rows that belong in the result. For a ±0.0 threshold use a non-strict
bound and stop filtering on later sort keys.

Closes apache#26618

Co-Authored-By: Claude Opus 5.5 <[email protected]>
statement ok
set datafusion.execution.target_partitions = 4;

# The TopK dynamic filter must keep `-0.0` rows when the threshold is `0.0`

@mkleen mkleen Oct 30, 2026 •

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.

The tests expect an order we plan to change. The expected rows depend on -0.0 sorting before 0.0. Under #26138, -0.0 and 0.0 will count as equal when sorting, so y decides the order. Then ORDER BY x, y LIMIT 2 should return (0,1), (-0,5) rather than (-0,5), (-0,7), and these tests will fail. That's fine for now, since the bug only happens with today's order. Could you add a comment above the tests so the next person knows why they changed? Something like:

# The expected rows assume -0.0 sorts before 0.0 (the current order).
# They will change when -0.0 and 0.0 are treated as equal when sorting,
# see https://github.com/apache/datafusion/issues/26138

@mkleen mkleen left a comment

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.

Thanks for working on this, and the fix makes sense. There is now a common place for this kind of check: datafusion_common::float_semantics, added in #26595. Putting the zero check there would keep all of this logic together.

@mkleen mkleen left a comment

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.

Thanks for working on this, and the fix makes sense. There is now a common place for this kind of check: datafusion_common::float_semantics, added in #26595. Putting the zero check there would keep all of this logic together.

@LiuYC3213687107

Copy link
Copy Markdown
Author

Thanks for the review! Done in 881738e: moved the zero check into datafusion_common::float_semantics as is_float_zero (with a unit test), and added the comment above the tests pointing to #26138.

@github-actions github-actions Bot added the common Related to common crate label Oct 31, 2026
@mkleen

mkleen commented Oct 31, 2026

Copy link
Copy Markdown
Contributor

@LiuYC3213687107 Thanks. While having a second look at this I think this would fit better to move to ScalarValue::is_float_zero similiar to #26727

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@LiuYC3213687107

Copy link
Copy Markdown
Author

Makes sense, thanks! Moved it to ScalarValue::is_float_zero next to normalize in da40aea, with the unit test in scalar/mod.rs.

@mkleen mkleen left a comment

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.

This looks good to me. I think the fix makes sense to merge as a correctness fix now.
The underlying problem is that sorting puts -0.0 before 0.0 while equality treats them as the same value. #26138 is suppose to fix this by merging -0.0 and 0.0 together. This should be marked temporary and linked to #26138.

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

Labels

common Related to common crate physical-plan Changes to the physical-plan crate sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrong results: ORDER BY <double>, ... LIMIT n drops -0.0 rows when 0.0 arrives in an earlier batch (TopK dynamic filter)

2 participants