What happened?
SQL AVG rounds every result to 10 significant digits, so an average over a single row does not equal that row. On BIGINT it can come back with the opposite sign.
BeamBuiltinAggregations.java:93
private static MathContext mc = new MathContext(10, RoundingMode.HALF_UP);
:468
protected BigDecimal prepareOutput(KV<Integer, BigDecimal> accumulator) {
return accumulator.getValue().divide(new BigDecimal(accumulator.getKey()), mc);
}
Every AVG subtype (:481 INT32, :493 INT64, :505 INT16, :517 INT8, :529 FLOAT, :541 DOUBLE, :553 DECIMAL) goes through it.
Running that division directly, one row in:
| type |
input |
AVG returns |
BIGINT |
9223372036854775807 |
-9223372036709551616 — sign flipped |
BIGINT |
-9223372036854775808 |
9223372036709551616 — sign flipped |
BIGINT |
1786500000123 |
1786500000000 — last three digits zeroed |
DECIMAL |
123456789.99 |
123456790.0 |
The third row is the one that makes this ordinary rather than exotic: any id, epoch-millis or cent-denominated amount above 10 digits is silently rounded.
Why the path is live
BeamRuleSets.java:117 has // CoreRules.AGGREGATE_REDUCE_FUNCTIONS commented out, so AVG is not rewritten into SUM/COUNT and survives to BeamAggregationRel → AggregationCombineFnAdapter.createCombineFn → BeamBuiltinAggregations.create.
BeamRelDataTypeSystem does not override deriveAvgAggType, so Calcite's default applies and the declared output type is the argument type.
Why I am not sending a patch
The obvious change — MathContext.DECIMAL128, or dropping the MathContext — is also wrong, just differently:
AVG over DECIMAL(18,2) should be DECIMAL(18,2) per SQL and per Calcite's deriveAvgAggType. DECIMAL128 gives scale 33 for {1,1,2}, which leaks 34-digit noise into user Rows where BigDecimal.equals is scale-sensitive.
- Dropping the
MathContext entirely turns a non-terminating division such as AVG of {1,2} over 3 rows into ArithmeticException.
- Some
DOUBLE results get uglier: AVG of {0.1, 0.2} is 0.15 today and 0.15000000000000002 without the rounding.
The fix that actually matches the declared type looks like threading the output RelDataType's precision and scale from AggregateCall into the CombineFn. That information exists at AggregationCombineFnAdapter:142 but is discarded — only the unparameterised field.getType() reaches BeamBuiltinAggregations.create. Changing that touches AVG, VAR_*, STDDEV_*, COVAR_* and their coders, which is a design call for someone who owns this area rather than something to bolt on.
Happy to implement whichever direction a maintainer prefers.
Note
#39507 is open against this same file (Add SINGLE_VALUE aggregate function), so whoever picks this up may want to sequence after it.
Issue Priority
Priority: 2 (default / most bugs should be filed as P2)
Issue Components
What happened?
SQL
AVGrounds every result to 10 significant digits, so an average over a single row does not equal that row. OnBIGINTit can come back with the opposite sign.BeamBuiltinAggregations.java:93:468Every AVG subtype (
:481INT32,:493INT64,:505INT16,:517INT8,:529FLOAT,:541DOUBLE,:553DECIMAL) goes through it.Running that division directly, one row in:
AVGreturnsBIGINT9223372036854775807-9223372036709551616— sign flippedBIGINT-92233720368547758089223372036709551616— sign flippedBIGINT17865000001231786500000000— last three digits zeroedDECIMAL123456789.99123456790.0The third row is the one that makes this ordinary rather than exotic: any id, epoch-millis or cent-denominated amount above 10 digits is silently rounded.
Why the path is live
BeamRuleSets.java:117has// CoreRules.AGGREGATE_REDUCE_FUNCTIONScommented out, soAVGis not rewritten intoSUM/COUNTand survives toBeamAggregationRel→AggregationCombineFnAdapter.createCombineFn→BeamBuiltinAggregations.create.BeamRelDataTypeSystemdoes not overridederiveAvgAggType, so Calcite's default applies and the declared output type is the argument type.Why I am not sending a patch
The obvious change —
MathContext.DECIMAL128, or dropping theMathContext— is also wrong, just differently:AVGoverDECIMAL(18,2)should beDECIMAL(18,2)per SQL and per Calcite'sderiveAvgAggType.DECIMAL128gives scale 33 for{1,1,2}, which leaks 34-digit noise into userRows whereBigDecimal.equalsis scale-sensitive.MathContextentirely turns a non-terminating division such asAVGof{1,2}over 3 rows intoArithmeticException.DOUBLEresults get uglier:AVGof{0.1, 0.2}is0.15today and0.15000000000000002without the rounding.The fix that actually matches the declared type looks like threading the output
RelDataType's precision and scale fromAggregateCallinto theCombineFn. That information exists atAggregationCombineFnAdapter:142but is discarded — only the unparameterisedfield.getType()reachesBeamBuiltinAggregations.create. Changing that touchesAVG,VAR_*,STDDEV_*,COVAR_*and their coders, which is a design call for someone who owns this area rather than something to bolt on.Happy to implement whichever direction a maintainer prefers.
Note
#39507 is open against this same file (
Add SINGLE_VALUE aggregate function), so whoever picks this up may want to sequence after it.Issue Priority
Priority: 2 (default / most bugs should be filed as P2)
Issue Components