1616// under the License.
1717
1818use arrow:: {
19- array:: { ArrayRef , ArrowNumericType } ,
20- datatypes:: {
21- Decimal32Type , Decimal64Type , Decimal128Type , Decimal256Type , DecimalType , i256,
22- } ,
19+ array:: { ArrayRef , ArrowNativeTypeOp , ArrowNumericType } ,
20+ compute:: DecimalCast ,
21+ datatypes:: { ArrowNativeType , DecimalType } ,
2322} ;
24- use datafusion_common:: { Result , ScalarValue } ;
23+ use datafusion_common:: { Result , ScalarValue , exec_datafusion_err , exec_err } ;
2524use datafusion_expr_common:: accumulator:: Accumulator ;
2625use std:: fmt:: Debug ;
26+ use std:: marker:: PhantomData ;
2727use std:: mem:: size_of_val;
2828
2929use crate :: aggregate:: sum_distinct:: DistinctSumAccumulator ;
3030use crate :: utils:: DecimalAverager ;
3131
3232/// Generic implementation of `AVG DISTINCT` for Decimal types.
3333/// Handles both all Arrow decimal types (32, 64, 128 and 256 bits).
34+ ///
35+ /// The distinct values are stored in the input type `I`; only the intermediate
36+ /// sum is computed in the (never narrower) sum type `S` so it cannot overflow
37+ /// `I`'s native type.
3438#[ derive( Debug ) ]
35- pub struct DecimalDistinctAvgAccumulator < T : DecimalType + Debug > {
36- sum_accumulator : DistinctSumAccumulator < T > ,
39+ pub struct DecimalDistinctAvgAccumulator <
40+ I : DecimalType + Debug ,
41+ S : DecimalType + Debug = I ,
42+ > {
43+ sum_accumulator : DistinctSumAccumulator < I > ,
3744 sum_scale : i8 ,
3845 target_precision : u8 ,
3946 target_scale : i8 ,
47+ _sum_type : PhantomData < S > ,
4048}
4149
42- impl < T : DecimalType + Debug > DecimalDistinctAvgAccumulator < T > {
50+ impl < I : DecimalType + Debug , S : DecimalType + Debug > DecimalDistinctAvgAccumulator < I , S > {
4351 pub fn with_decimal_params (
4452 sum_scale : i8 ,
4553 target_precision : u8 ,
4654 target_scale : i8 ,
4755 ) -> Self {
48- let data_type = T :: TYPE_CONSTRUCTOR ( T :: MAX_PRECISION , sum_scale) ;
56+ let data_type = I :: TYPE_CONSTRUCTOR ( I :: MAX_PRECISION , sum_scale) ;
4957
5058 Self {
5159 sum_accumulator : DistinctSumAccumulator :: new ( & data_type) ,
5260 sum_scale,
5361 target_precision,
5462 target_scale,
63+ _sum_type : PhantomData ,
5564 }
5665 }
5766}
5867
59- impl < T : DecimalType + ArrowNumericType + Debug > Accumulator
60- for DecimalDistinctAvgAccumulator < T >
68+ impl < I , S > Accumulator for DecimalDistinctAvgAccumulator < I , S >
69+ where
70+ I : DecimalType + ArrowNumericType + Debug ,
71+ S : DecimalType + ArrowNumericType + Debug ,
72+ I :: Native : Into < S :: Native > + DecimalCast ,
73+ S :: Native : DecimalCast ,
6174{
6275 fn state ( & mut self ) -> Result < Vec < ScalarValue > > {
6376 self . sum_accumulator . state ( )
@@ -72,78 +85,43 @@ impl<T: DecimalType + ArrowNumericType + Debug> Accumulator
7285 }
7386
7487 fn evaluate ( & mut self ) -> Result < ScalarValue > {
75- if self . sum_accumulator . distinct_count ( ) == 0 {
76- return ScalarValue :: new_primitive :: < T > (
77- None ,
78- & T :: TYPE_CONSTRUCTOR ( self . target_precision , self . target_scale ) ,
79- ) ;
88+ let out_type = I :: TYPE_CONSTRUCTOR ( self . target_precision , self . target_scale ) ;
89+ let count = self . sum_accumulator . distinct_count ( ) ;
90+ if count == 0 {
91+ return ScalarValue :: new_primitive :: < I > ( None , & out_type) ;
8092 }
8193
82- let sum_scalar = self . sum_accumulator . evaluate ( ) ?;
83-
84- match sum_scalar {
85- ScalarValue :: Decimal32 ( Some ( sum) , _, _) => {
86- let decimal_averager = DecimalAverager :: < Decimal32Type > :: try_new (
87- self . sum_scale ,
88- self . target_precision ,
89- self . target_scale ,
90- ) ?;
91- let avg = decimal_averager
92- . avg ( sum, self . sum_accumulator . distinct_count ( ) as i32 ) ?;
93- Ok ( ScalarValue :: Decimal32 (
94- Some ( avg) ,
95- self . target_precision ,
96- self . target_scale ,
97- ) )
98- }
99- ScalarValue :: Decimal64 ( Some ( sum) , _, _) => {
100- let decimal_averager = DecimalAverager :: < Decimal64Type > :: try_new (
101- self . sum_scale ,
102- self . target_precision ,
103- self . target_scale ,
104- ) ?;
105- let avg = decimal_averager
106- . avg ( sum, self . sum_accumulator . distinct_count ( ) as i64 ) ?;
107- Ok ( ScalarValue :: Decimal64 (
108- Some ( avg) ,
109- self . target_precision ,
110- self . target_scale ,
111- ) )
112- }
113- ScalarValue :: Decimal128 ( Some ( sum) , _, _) => {
114- let decimal_averager = DecimalAverager :: < Decimal128Type > :: try_new (
115- self . sum_scale ,
116- self . target_precision ,
117- self . target_scale ,
118- ) ?;
119- let avg = decimal_averager
120- . avg ( sum, self . sum_accumulator . distinct_count ( ) as i128 ) ?;
121- Ok ( ScalarValue :: Decimal128 (
122- Some ( avg) ,
123- self . target_precision ,
124- self . target_scale ,
125- ) )
126- }
127- ScalarValue :: Decimal256 ( Some ( sum) , _, _) => {
128- let decimal_averager = DecimalAverager :: < Decimal256Type > :: try_new (
129- self . sum_scale ,
130- self . target_precision ,
131- self . target_scale ,
132- ) ?;
133- // `distinct_count` returns `u64`, but `avg` expects `i256`
134- // first convert `u64` to `i128`, then convert `i128` to `i256` to avoid overflow
135- let distinct_cnt: i128 = self . sum_accumulator . distinct_count ( ) as i128 ;
136- let count: i256 = i256:: from_i128 ( distinct_cnt) ;
137- let avg = decimal_averager. avg ( sum, count) ?;
138- Ok ( ScalarValue :: Decimal256 (
139- Some ( avg) ,
140- self . target_precision ,
141- self . target_scale ,
142- ) )
143- }
144-
145- _ => unreachable ! ( "Unsupported decimal type: {:?}" , sum_scalar) ,
94+ // Sum the distinct input values in the wider `S` so the total cannot
95+ // overflow the input's native width (mirrors the non-distinct path).
96+ let mut sum = S :: Native :: usize_as ( 0 ) ;
97+ for value in self . sum_accumulator . distinct_values ( ) {
98+ sum = sum. add_wrapping ( value. into ( ) ) ;
14699 }
100+
101+ let Some ( count) = S :: Native :: from_usize ( count) else {
102+ return exec_err ! (
103+ "Arithmetic overflow in avg: the distinct count {count} cannot \
104+ be represented in the sum type"
105+ ) ;
106+ } ;
107+
108+ let averager = DecimalAverager :: < S > :: try_new (
109+ self . sum_scale ,
110+ self . target_precision ,
111+ self . target_scale ,
112+ ) ?;
113+ // Narrowing the average back to the (never wider) output type cannot
114+ // fail in practice: `DecimalAverager::avg` validates the average
115+ // against the output precision, whose bound fits the output's native
116+ // type by construction
117+ let avg =
118+ I :: Native :: from_decimal ( averager. avg ( sum, count) ?) . ok_or_else ( || {
119+ exec_datafusion_err ! (
120+ "Arithmetic overflow in avg: the computed average does not fit \
121+ the output type"
122+ )
123+ } ) ?;
124+ ScalarValue :: new_primitive :: < I > ( Some ( avg) , & out_type)
147125 }
148126
149127 fn size ( & self ) -> usize {
@@ -160,6 +138,9 @@ mod tests {
160138 use arrow:: array:: {
161139 Decimal32Array , Decimal64Array , Decimal128Array , Decimal256Array ,
162140 } ;
141+ use arrow:: datatypes:: {
142+ Decimal32Type , Decimal64Type , Decimal128Type , Decimal256Type , i256,
143+ } ;
163144 use std:: sync:: Arc ;
164145
165146 #[ test]
@@ -279,4 +260,94 @@ mod tests {
279260
280261 Ok ( ( ) )
281262 }
263+
264+ // The overflow regression tests below use odd-count ranges symmetric
265+ // around a center value, so the exact sum is `count * center` and the
266+ // average is exactly `center`.
267+
268+ #[ test]
269+ fn test_decimal32_distinct_avg_widens_to_decimal64 ( ) -> Result < ( ) > {
270+ // 42951 distinct values centered on 50000:
271+ // sum = 42951 * 50000 = 2,147,550,000 > i32::MAX
272+ let array = Decimal32Array :: from_iter_values ( 28525 ..=71475 )
273+ . with_precision_and_scale ( 5 , 0 ) ?;
274+
275+ let mut accumulator = DecimalDistinctAvgAccumulator :: <
276+ Decimal32Type ,
277+ Decimal64Type ,
278+ > :: with_decimal_params ( 0 , 9 , 4 ) ;
279+ accumulator. update_batch ( & [ Arc :: new ( array) ] ) ?;
280+
281+ assert_eq ! (
282+ accumulator. evaluate( ) ?,
283+ ScalarValue :: Decimal32 ( Some ( 500_000_000 ) , 9 , 4 )
284+ ) ;
285+
286+ Ok ( ( ) )
287+ }
288+
289+ #[ test]
290+ fn test_decimal32_distinct_avg_widens_to_decimal128 ( ) -> Result < ( ) > {
291+ // 21477 distinct values centered on 99999:
292+ // sum = 21477 * 99999 = 2,147,678,523 > i32::MAX
293+ let array = Decimal32Array :: from_iter_values ( 89261 ..=110737 )
294+ . with_precision_and_scale ( 9 , 0 ) ?;
295+
296+ let mut accumulator = DecimalDistinctAvgAccumulator :: <
297+ Decimal32Type ,
298+ Decimal128Type ,
299+ > :: with_decimal_params ( 0 , 9 , 4 ) ;
300+ accumulator. update_batch ( & [ Arc :: new ( array) ] ) ?;
301+
302+ assert_eq ! (
303+ accumulator. evaluate( ) ?,
304+ ScalarValue :: Decimal32 ( Some ( 999_990_000 ) , 9 , 4 )
305+ ) ;
306+
307+ Ok ( ( ) )
308+ }
309+
310+ #[ test]
311+ fn test_decimal64_distinct_avg_widens_to_decimal128 ( ) -> Result < ( ) > {
312+ // 92235 distinct values centered on 10^14 - 1:
313+ // sum = 92235 * (10^14 - 1) ~= 9.22e18 > i64::MAX
314+ let center: i64 = 100_000_000_000_000 - 1 ;
315+ let array = Decimal64Array :: from_iter_values ( center - 46117 ..=center + 46117 )
316+ . with_precision_and_scale ( 18 , 0 ) ?;
317+
318+ let mut accumulator = DecimalDistinctAvgAccumulator :: <
319+ Decimal64Type ,
320+ Decimal128Type ,
321+ > :: with_decimal_params ( 0 , 18 , 4 ) ;
322+ accumulator. update_batch ( & [ Arc :: new ( array) ] ) ?;
323+
324+ assert_eq ! (
325+ accumulator. evaluate( ) ?,
326+ ScalarValue :: Decimal64 ( Some ( 999_999_999_999_990_000 ) , 18 , 4 )
327+ ) ;
328+
329+ Ok ( ( ) )
330+ }
331+
332+ #[ test]
333+ fn test_decimal128_distinct_avg_widens_to_decimal256 ( ) -> Result < ( ) > {
334+ // 21477 distinct values ending at 10^34 - 1, centered on 10^34 - 10739:
335+ // sum = 21477 * (10^34 - 10739) ~= 2.15e38 > i128::MAX
336+ let center: i128 = 10_i128 . pow ( 34 ) - 10739 ;
337+ let array = Decimal128Array :: from_iter_values ( center - 10738 ..=center + 10738 )
338+ . with_precision_and_scale ( 34 , 0 ) ?;
339+
340+ let mut accumulator = DecimalDistinctAvgAccumulator :: <
341+ Decimal128Type ,
342+ Decimal256Type ,
343+ > :: with_decimal_params ( 0 , 38 , 4 ) ;
344+ accumulator. update_batch ( & [ Arc :: new ( array) ] ) ?;
345+
346+ assert_eq ! (
347+ accumulator. evaluate( ) ?,
348+ ScalarValue :: Decimal128 ( Some ( center * 10_000 ) , 38 , 4 )
349+ ) ;
350+
351+ Ok ( ( ) )
352+ }
282353}
0 commit comments