Skip to main content

datafusion_functions_aggregate_common/aggregate/avg_distinct/
decimal.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use arrow::{
19    array::{ArrayRef, ArrowNativeTypeOp, ArrowNumericType},
20    compute::DecimalCast,
21    datatypes::{ArrowNativeType, DecimalType},
22};
23use datafusion_common::{Result, ScalarValue, exec_datafusion_err, exec_err};
24use datafusion_expr_common::accumulator::Accumulator;
25use std::fmt::Debug;
26use std::marker::PhantomData;
27use std::mem::size_of_val;
28
29use crate::aggregate::sum_distinct::DistinctSumAccumulator;
30use crate::utils::DecimalAverager;
31
32/// Generic implementation of `AVG DISTINCT` for Decimal types.
33/// 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.
38#[derive(Debug)]
39pub struct DecimalDistinctAvgAccumulator<
40    I: DecimalType + Debug,
41    S: DecimalType + Debug = I,
42> {
43    sum_accumulator: DistinctSumAccumulator<I>,
44    sum_scale: i8,
45    target_precision: u8,
46    target_scale: i8,
47    _sum_type: PhantomData<S>,
48}
49
50impl<I: DecimalType + Debug, S: DecimalType + Debug> DecimalDistinctAvgAccumulator<I, S> {
51    pub fn with_decimal_params(
52        sum_scale: i8,
53        target_precision: u8,
54        target_scale: i8,
55    ) -> Self {
56        let data_type = I::TYPE_CONSTRUCTOR(I::MAX_PRECISION, sum_scale);
57
58        Self {
59            sum_accumulator: DistinctSumAccumulator::new(&data_type),
60            sum_scale,
61            target_precision,
62            target_scale,
63            _sum_type: PhantomData,
64        }
65    }
66}
67
68impl<I, S> Accumulator for DecimalDistinctAvgAccumulator<I, S>
69where
70    I: DecimalType + ArrowNumericType + Debug,
71    S: DecimalType + ArrowNumericType + Debug,
72    I::Native: Into<S::Native> + DecimalCast,
73    S::Native: DecimalCast,
74{
75    fn state(&mut self) -> Result<Vec<ScalarValue>> {
76        self.sum_accumulator.state()
77    }
78
79    fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
80        self.sum_accumulator.update_batch(values)
81    }
82
83    fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
84        self.sum_accumulator.merge_batch(states)
85    }
86
87    fn evaluate(&mut self) -> Result<ScalarValue> {
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);
92        }
93
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());
99        }
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)
125    }
126
127    fn size(&self) -> usize {
128        let fixed_size = size_of_val(self);
129
130        // Account for the size of the sum_accumulator with its contained values
131        fixed_size + self.sum_accumulator.size()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use arrow::array::{
139        Decimal32Array, Decimal64Array, Decimal128Array, Decimal256Array,
140    };
141    use arrow::datatypes::{
142        Decimal32Type, Decimal64Type, Decimal128Type, Decimal256Type, i256,
143    };
144    use std::sync::Arc;
145
146    #[test]
147    fn test_decimal32_distinct_avg_accumulator() -> Result<()> {
148        let precision = 5_u8;
149        let scale = 2_i8;
150        let array = Decimal32Array::from(vec![
151            Some(10_00),
152            Some(12_50),
153            Some(17_50),
154            Some(20_00),
155            Some(20_00),
156            Some(30_00),
157            None,
158            None,
159        ])
160        .with_precision_and_scale(precision, scale)?;
161
162        let mut accumulator =
163            DecimalDistinctAvgAccumulator::<Decimal32Type>::with_decimal_params(
164                scale, 9, 6,
165            );
166        accumulator.update_batch(&[Arc::new(array)])?;
167
168        let result = accumulator.evaluate()?;
169        let expected_result = ScalarValue::Decimal32(Some(18000000), 9, 6);
170        assert_eq!(result, expected_result);
171
172        Ok(())
173    }
174
175    #[test]
176    fn test_decimal64_distinct_avg_accumulator() -> Result<()> {
177        let precision = 10_u8;
178        let scale = 4_i8;
179        let array = Decimal64Array::from(vec![
180            Some(100_0000),
181            Some(125_0000),
182            Some(175_0000),
183            Some(200_0000),
184            Some(200_0000),
185            Some(300_0000),
186            None,
187            None,
188        ])
189        .with_precision_and_scale(precision, scale)?;
190
191        let mut accumulator =
192            DecimalDistinctAvgAccumulator::<Decimal64Type>::with_decimal_params(
193                scale, 14, 8,
194            );
195        accumulator.update_batch(&[Arc::new(array)])?;
196
197        let result = accumulator.evaluate()?;
198        let expected_result = ScalarValue::Decimal64(Some(180_00000000), 14, 8);
199        assert_eq!(result, expected_result);
200
201        Ok(())
202    }
203
204    #[test]
205    fn test_decimal128_distinct_avg_accumulator() -> Result<()> {
206        let precision = 10_u8;
207        let scale = 4_i8;
208        let array = Decimal128Array::from(vec![
209            Some(100_0000),
210            Some(125_0000),
211            Some(175_0000),
212            Some(200_0000),
213            Some(200_0000),
214            Some(300_0000),
215            None,
216            None,
217        ])
218        .with_precision_and_scale(precision, scale)?;
219
220        let mut accumulator =
221            DecimalDistinctAvgAccumulator::<Decimal128Type>::with_decimal_params(
222                scale, 14, 8,
223            );
224        accumulator.update_batch(&[Arc::new(array)])?;
225
226        let result = accumulator.evaluate()?;
227        let expected_result = ScalarValue::Decimal128(Some(180_00000000), 14, 8);
228        assert_eq!(result, expected_result);
229
230        Ok(())
231    }
232
233    #[test]
234    fn test_decimal256_distinct_avg_accumulator() -> Result<()> {
235        let precision = 50_u8;
236        let scale = 2_i8;
237
238        let array = Decimal256Array::from(vec![
239            Some(i256::from_i128(10_000)),
240            Some(i256::from_i128(12_500)),
241            Some(i256::from_i128(17_500)),
242            Some(i256::from_i128(20_000)),
243            Some(i256::from_i128(20_000)),
244            Some(i256::from_i128(30_000)),
245            None,
246            None,
247        ])
248        .with_precision_and_scale(precision, scale)?;
249
250        let mut accumulator =
251            DecimalDistinctAvgAccumulator::<Decimal256Type>::with_decimal_params(
252                scale, 54, 6,
253            );
254        accumulator.update_batch(&[Arc::new(array)])?;
255
256        let result = accumulator.evaluate()?;
257        let expected_result =
258            ScalarValue::Decimal256(Some(i256::from_i128(180_000000)), 54, 6);
259        assert_eq!(result, expected_result);
260
261        Ok(())
262    }
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    }
353}