Skip to content

[Bug] width_bucket returns 0 when num_buckets > (max_value - min_value) #68222

Open

Description

@yy782

Search before asking

  • I had searched in the issues and found no similar issues.

Version

master
CommitID: 979e053

What's Wrong?

mysql> select width_bucket (9, 0, 10, 20);
+-----------------------------+
| width_bucket (9, 0, 10, 20) |
+-----------------------------+
| 0 |
+-----------------------------+
1 row in set (0.02 sec)

mysql> select width_bucket (3, 0, 10, 20);
+-----------------------------+
| width_bucket (3, 0, 10, 20) |
+-----------------------------+
| 0 |
+-----------------------------+
1 row in set (0.02 sec)

mysql> select width_bucket (1, 0, 2, 5);
+---------------------------+
| width_bucket (1, 0, 2, 5) |
+---------------------------+
| 0 |
+---------------------------+
1 row in set (0.01 sec)

mysql> select width_bucket (0, 0, 10, 20);
+-----------------------------+
| width_bucket (0, 0, 10, 20) |
+-----------------------------+
| 0 |
+-----------------------------+
1 row in set (0.02 sec)

What You Expected?

mysql> select width_bucket (9, 0, 10, 20);
+-----------------------------+
| width_bucket (9, 0, 10, 20) |
+-----------------------------+
| 19 |
+-----------------------------+
1 row in set (0.02 sec)

mysql> select width_bucket (3, 0, 10, 20);
+-----------------------------+
| width_bucket (3, 0, 10, 20) |
+-----------------------------+
| 7 |
+-----------------------------+
1 row in set (0.02 sec)

mysql> select width_bucket (1, 0, 2, 5);
+---------------------------+
| width_bucket (1, 0, 2, 5) |
+---------------------------+
| 3 |
+---------------------------+
1 row in set (0.01 sec)

mysql> select width_bucket (0, 0, 10, 20);
+-----------------------------+
| width_bucket (0, 0, 10, 20) |
+-----------------------------+
| 1 |
+-----------------------------+
1 row in set (0.02 sec)

How to Reproduce?

Run the following SQL directly (no table required):

select width_bucket(9, 0, 10, 20); -- returns 0
select width_bucket(3, 0, 10, 20); -- returns 0
select width_bucket(1, 0, 2, 5); -- returns 0

All three should return 19 / 7 / 3 respectively.

Anything Else?

No response

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Activity

  1. Doris-Breakwater commented on Sep 19, 2026

    @Doris-Breakwater
    Collaborator

    Breakwater-GitHub-Analysis-Slot: slot_5b7a1add75c6

    Initial assessment

    This is a confirmed deterministic correctness bug in the BE width_bucket implementation at commit 979e053f0802fbc48f83dafe7f713c02968415a8. The issue is currently open with no labels; the supplied commit and scalar SQL are sufficient for initial triage.

    Verified root cause

    In FunctionWidthBucket::_execute, average_value is correctly computed with floating-point arithmetic:

    auto average_value = (max_value - min_value) / (1.0 * num_buckets);

    However, the in-range branch then checks the width again using the input type's arithmetic:

    if ((max_value - min_value) / num_buckets == 0) {
        continue;
    }

    For the integral overloads, this is integer division. For example, 10 / 20 and 2 / 5 truncate to 0; continue leaves the pre-zeroed result unchanged. That directly explains every reported result, including width_bucket(0, 0, 10, 20): an expression equal to the lower bound reaches the stale guard and incorrectly remains bucket 0 instead of bucket 1.

    The confirmed scope is the TINYINT, SMALLINT, INT, and BIGINT execution paths when 0 < max_value - min_value < num_buckets and the expression is in [min_value, max_value). Values below min_value and values at/above max_value take earlier branches and are not affected by this guard. The same integer-truncation mechanism does not apply to the floating-point overloads.

    History supports this diagnosis: the guard protected the original integer denominator. PR #24673 changed the actual bucket calculation to divide by the floating-point average_value, but retained the old integer zero check. Existing regression coverage also misses this shape: the direct integer case uses a span of 11 with 10 buckets, while the generated integral cases use a span of 1 with 1 bucket.

    Missing information

    No additional logs, profile, or environment details are required to establish this issue. The version, exact commit, deterministic reproduction, and expected values are all present. I did not run a Doris cluster or a candidate patch during this triage; the conclusion is based on the reported runtime output and inspection of the exact commit.

    Recommended next steps

    1. In the reporter's proposed PR, remove the stale integral-division guard or make any zero-width validation use the same non-truncating calculation as average_value. Keep the existing below-range (0) and at/above-range (num_buckets + 1) behavior unchanged.
    2. Add the four reported constant queries as regression cases in test_width_bucket_function.groovy, including the lower-bound case.
    3. Add explicit-cast coverage for all four integral overloads, plus expr = min_value, an in-range value near max_value, and the existing out-of-range boundaries. This prevents the fix from only covering the literal type selected by the analyzer.
    4. Run the focused test_width_bucket_function regression suite and the existing Nereids scalar-function coverage before merge.

    The fix direction above is code-derived but has not yet been implemented or test-validated here. Equal or descending bounds are outside the reported failure and should not have their semantics changed incidentally.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions