Nice catch! Could we cherry-pick this into the rc/2.0.11 branch?

Xuan Wang

Yuan Tian <[email protected]> 于2026年7月24日周五 17:04写道:

> Hi all,
>
>
> I found an issue in the table-model grouped MAX aggregation for FLOAT
> and DOUBLE values.
>
> Bug description
> ---------------
>
> When all values in a group are zero, MAX returns NULL instead of 0.
> MIN and AVG return the expected result, and MAX_BY(value, value) also
> works correctly.
>
> The issue is not limited to zero. It affects any FLOAT or DOUBLE group
> whose maximum value is non-positive, including groups containing only
> negative values.
>
> For example:
>
>   SELECT device_id, MAX(value), MIN(value), AVG(value)
>   FROM table1
>   GROUP BY device_id;
>
> If all value entries for a device are 0, the result is:
>
>   MAX(value) = NULL
>   MIN(value) = 0
>   AVG(value) = 0
>
> Root cause
> ----------
>
> GroupedMaxAccumulator used the following initial values:
>
>   new FloatBigArray(Float.MIN_VALUE)
>   new DoubleBigArray(Double.MIN_VALUE)
>
> In Java, Float.MIN_VALUE and Double.MIN_VALUE are the smallest
> positive non-zero values, rather than the most negative representable
> values.
>
> The grouped accumulator updates its state using logic equivalent to:
>
>   if (value >= currentMax) {
>     inits.set(groupId, true);
>     currentMax = value;
>   }
>
> Therefore, when every input value is zero or negative, the condition
> is never satisfied. The group's initialization flag remains false, and
> evaluateFinal() treats the group as empty and emits NULL.
>
> This explains why positive values work and why MAX_BY is unaffected.
> MAX_BY explicitly checks whether the group has been initialized before
> comparing values.
>
> Affected scope
> --------------
>
> The issue affects table-model grouped MAX(FLOAT) and MAX(DOUBLE) when
> the query uses GroupedMaxAccumulator, such as the hash or
> streaming-hash aggregation path.
>
> The non-grouped MaxAccumulator and the tree-model MAX_VALUE
> implementation use separate initialization handling and are not
> affected.
>
> Proposed fix
> ------------
>
> Initialize the floating-point maximum state with negative infinity:
>
>   new FloatBigArray(Float.NEGATIVE_INFINITY)
>   new DoubleBigArray(Double.NEGATIVE_INFINITY)
>
> This preserves the existing sentinel-based update path while correctly
> handling zero, negative values, and negative infinity. Truly empty
> groups still return NULL because their initialization flags remain
> false.
>
> Validation
> ----------
>
> The fix includes:
>
> - Unit tests for FLOAT and DOUBLE values containing zero, negative
> values, and negative infinity.
> - Coverage for both raw input and intermediate aggregation input.
> - Verification that groups without input still return NULL.
> - A table-model integration test covering the non-stream grouped
> aggregation path.
>
> The targeted unit and integration tests passed, along with a clean
> 37-module reactor build.
>
> I opened a draft PR here:
> https://github.com/apache/iotdb/pull/18300
>
> Feedback is welcome.
>
> Best regards,
> Yuan Tian
>

Reply via email to