1use arrow::array::{Array, ArrayRef, ArrowNativeTypeOp, ArrowNumericType, AsArray};
21use arrow::datatypes::Field;
22use arrow::datatypes::{
23 ArrowNativeType, DECIMAL32_MAX_PRECISION, DECIMAL64_MAX_PRECISION,
24 DECIMAL128_MAX_PRECISION, DECIMAL256_MAX_PRECISION, DataType, Decimal32Type,
25 Decimal64Type, Decimal128Type, Decimal256Type, DurationMicrosecondType,
26 DurationMillisecondType, DurationNanosecondType, DurationSecondType, FieldRef,
27 Float64Type, Int64Type, IntervalDayTimeType, IntervalMonthDayNanoType, IntervalUnit,
28 IntervalYearMonthType, TimeUnit, UInt64Type,
29};
30use datafusion_common::hash_utils::RandomState;
31use datafusion_common::internal_err;
32use datafusion_common::stats::Precision;
33use datafusion_common::types::{
34 NativeType, logical_float64, logical_int8, logical_int16, logical_int32,
35 logical_int64, logical_uint8, logical_uint16, logical_uint32, logical_uint64,
36};
37use datafusion_common::{HashMap, Result, ScalarValue, exec_err, not_impl_err};
38use datafusion_expr::expr::AggregateFunction;
39use datafusion_expr::expr_fn::cast;
40use datafusion_expr::function::{AccumulatorArgs, StateFieldsArgs};
41use datafusion_expr::utils::{AggregateOrderSensitivity, format_state_name};
42use datafusion_expr::{
43 Accumulator, AggregateUDFImpl, Coercion, Documentation, Expr, GroupsAccumulator,
44 Operator, ReversedUDAF, SetMonotonicity, Signature, StatisticsArgs, TypeSignature,
45 TypeSignatureClass, Volatility,
46};
47use datafusion_functions_aggregate_common::aggregate::groups_accumulator::prim_op::PrimitiveGroupsAccumulator;
48use datafusion_functions_aggregate_common::aggregate::sum_distinct::DistinctSumAccumulator;
49use datafusion_macros::user_doc;
50use datafusion_physical_expr::expressions::{CastExpr, Column};
51use std::mem::{size_of, size_of_val};
52
53make_udaf_expr_and_func!(
54 Sum,
55 sum,
56 expression,
57 "Returns the sum of a group of values.",
58 sum_udaf
59);
60
61pub fn sum_distinct(expr: Expr) -> Expr {
62 Expr::AggregateFunction(AggregateFunction::new_udf(
63 sum_udaf(),
64 vec![expr],
65 true,
66 None,
67 vec![],
68 None,
69 ))
70}
71
72macro_rules! downcast_sum {
79 ($args:ident, $helper:ident) => {
80 match $args.return_field.data_type().clone() {
81 DataType::UInt64 => {
82 $helper!(UInt64Type, $args.return_field.data_type().clone())
83 }
84 DataType::Int64 => {
85 $helper!(Int64Type, $args.return_field.data_type().clone())
86 }
87 DataType::Float64 => {
88 $helper!(Float64Type, $args.return_field.data_type().clone())
89 }
90 DataType::Decimal32(_, _) => {
91 $helper!(Decimal32Type, $args.return_field.data_type().clone())
92 }
93 DataType::Decimal64(_, _) => {
94 $helper!(Decimal64Type, $args.return_field.data_type().clone())
95 }
96 DataType::Decimal128(_, _) => {
97 $helper!(Decimal128Type, $args.return_field.data_type().clone())
98 }
99 DataType::Decimal256(_, _) => {
100 $helper!(Decimal256Type, $args.return_field.data_type().clone())
101 }
102 DataType::Duration(TimeUnit::Second) => {
103 $helper!(DurationSecondType, $args.return_field.data_type().clone())
104 }
105 DataType::Duration(TimeUnit::Millisecond) => {
106 $helper!(
107 DurationMillisecondType,
108 $args.return_field.data_type().clone()
109 )
110 }
111 DataType::Duration(TimeUnit::Microsecond) => {
112 $helper!(
113 DurationMicrosecondType,
114 $args.return_field.data_type().clone()
115 )
116 }
117 DataType::Duration(TimeUnit::Nanosecond) => {
118 $helper!(
119 DurationNanosecondType,
120 $args.return_field.data_type().clone()
121 )
122 }
123 DataType::Interval(IntervalUnit::YearMonth) => {
124 $helper!(
125 IntervalYearMonthType,
126 $args.return_field.data_type().clone()
127 )
128 }
129 DataType::Interval(IntervalUnit::DayTime) => {
130 $helper!(IntervalDayTimeType, $args.return_field.data_type().clone())
131 }
132 DataType::Interval(IntervalUnit::MonthDayNano) => {
133 $helper!(
134 IntervalMonthDayNanoType,
135 $args.return_field.data_type().clone()
136 )
137 }
138 _ => {
139 not_impl_err!(
140 "Sum not supported for {}: {}",
141 $args.name,
142 $args.return_field.data_type()
143 )
144 }
145 }
146 };
147}
148
149#[user_doc(
150 doc_section(label = "General Functions"),
151 description = "Returns the sum of all values in the specified column.",
152 syntax_example = "sum(expression)",
153 sql_example = r#"```sql
154> SELECT sum(column_name) FROM table_name;
155+-----------------------+
156| sum(column_name) |
157+-----------------------+
158| 12345 |
159+-----------------------+
160```"#,
161 standard_argument(name = "expression",)
162)]
163#[derive(Debug, PartialEq, Eq, Hash)]
164pub struct Sum {
165 signature: Signature,
166}
167
168impl Sum {
169 pub fn new() -> Self {
170 Self {
171 signature: Signature::one_of(
174 vec![
175 TypeSignature::Coercible(vec![Coercion::new_exact(
176 TypeSignatureClass::Decimal,
177 )]),
178 TypeSignature::Coercible(vec![Coercion::new_implicit(
180 TypeSignatureClass::Native(logical_uint64()),
181 vec![
182 TypeSignatureClass::Native(logical_uint8()),
183 TypeSignatureClass::Native(logical_uint16()),
184 TypeSignatureClass::Native(logical_uint32()),
185 ],
186 NativeType::UInt64,
187 )]),
188 TypeSignature::Coercible(vec![Coercion::new_implicit(
190 TypeSignatureClass::Native(logical_int64()),
191 vec![
192 TypeSignatureClass::Native(logical_int8()),
193 TypeSignatureClass::Native(logical_int16()),
194 TypeSignatureClass::Native(logical_int32()),
195 ],
196 NativeType::Int64,
197 )]),
198 TypeSignature::Coercible(vec![Coercion::new_implicit(
200 TypeSignatureClass::Native(logical_float64()),
201 vec![TypeSignatureClass::Float],
202 NativeType::Float64,
203 )]),
204 TypeSignature::Coercible(vec![Coercion::new_exact(
205 TypeSignatureClass::Duration,
206 )]),
207 TypeSignature::Coercible(vec![Coercion::new_exact(
208 TypeSignatureClass::Interval,
209 )]),
210 ],
211 Volatility::Immutable,
212 ),
213 }
214 }
215}
216
217impl Default for Sum {
218 fn default() -> Self {
219 Self::new()
220 }
221}
222
223impl AggregateUDFImpl for Sum {
224 fn name(&self) -> &str {
225 "sum"
226 }
227
228 fn signature(&self) -> &Signature {
229 &self.signature
230 }
231
232 fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
233 match &arg_types[0] {
234 DataType::Int64 => Ok(DataType::Int64),
235 DataType::UInt64 => Ok(DataType::UInt64),
236 DataType::Float64 => Ok(DataType::Float64),
237 DataType::Decimal32(precision, scale) => {
240 let new_precision = DECIMAL32_MAX_PRECISION.min(*precision + 10);
241 Ok(DataType::Decimal32(new_precision, *scale))
242 }
243 DataType::Decimal64(precision, scale) => {
244 let new_precision = DECIMAL64_MAX_PRECISION.min(*precision + 10);
245 Ok(DataType::Decimal64(new_precision, *scale))
246 }
247 DataType::Decimal128(precision, scale) => {
248 let new_precision = DECIMAL128_MAX_PRECISION.min(*precision + 10);
249 Ok(DataType::Decimal128(new_precision, *scale))
250 }
251 DataType::Decimal256(precision, scale) => {
252 let new_precision = DECIMAL256_MAX_PRECISION.min(*precision + 10);
253 Ok(DataType::Decimal256(new_precision, *scale))
254 }
255 DataType::Duration(time_unit) => Ok(DataType::Duration(*time_unit)),
256 DataType::Interval(interval_unit) => Ok(DataType::Interval(*interval_unit)),
257 other => {
258 exec_err!("[return_type] SUM not supported for {}", other)
259 }
260 }
261 }
262
263 fn accumulator(&self, args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
264 if args.is_distinct {
265 macro_rules! helper {
266 ($t:ty, $dt:expr) => {
267 Ok(Box::new(DistinctSumAccumulator::<$t>::new(&$dt)))
268 };
269 }
270 downcast_sum!(args, helper)
271 } else {
272 macro_rules! helper {
273 ($t:ty, $dt:expr) => {
274 Ok(Box::new(SumAccumulator::<$t>::new($dt.clone())))
275 };
276 }
277 downcast_sum!(args, helper)
278 }
279 }
280
281 fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
282 if args.is_distinct {
283 Ok(vec![
284 Field::new_list(
285 format_state_name(args.name, "sum distinct"),
286 Field::new_list_field(args.return_type().clone(), true),
288 false,
289 )
290 .into(),
291 ])
292 } else {
293 Ok(vec![
294 Field::new(
295 format_state_name(args.name, "sum"),
296 args.return_type().clone(),
297 true,
298 )
299 .into(),
300 ])
301 }
302 }
303
304 fn groups_accumulator_supported(&self, args: AccumulatorArgs) -> bool {
305 !args.is_distinct
306 }
307
308 fn create_groups_accumulator(
309 &self,
310 args: AccumulatorArgs,
311 ) -> Result<Box<dyn GroupsAccumulator>> {
312 macro_rules! helper {
313 ($t:ty, $dt:expr) => {
314 Ok(Box::new(PrimitiveGroupsAccumulator::<$t, _>::new(
315 &$dt,
316 |x, y| *x = x.add_wrapping(y),
317 )))
318 };
319 }
320 downcast_sum!(args, helper)
321 }
322
323 fn create_sliding_accumulator(
324 &self,
325 args: AccumulatorArgs,
326 ) -> Result<Box<dyn Accumulator>> {
327 if args.is_distinct {
328 match args.return_field.data_type() {
332 DataType::Int64 => Ok(Box::new(SlidingDistinctSumAccumulator::try_new(
333 &DataType::Int64,
334 )?)),
335 _ => not_impl_err!(
336 "SUM(DISTINCT) over sliding window frames is only supported for Int64, got {}",
337 args.expr_fields[0].data_type()
338 ),
339 }
340 } else {
341 macro_rules! helper {
343 ($t:ty, $dt:expr) => {
344 Ok(Box::new(SlidingSumAccumulator::<$t>::new($dt.clone())))
345 };
346 }
347 downcast_sum!(args, helper)
348 }
349 }
350
351 fn reverse_expr(&self) -> ReversedUDAF {
352 ReversedUDAF::Identical
353 }
354
355 fn order_sensitivity(&self) -> AggregateOrderSensitivity {
356 AggregateOrderSensitivity::Insensitive
357 }
358
359 fn documentation(&self) -> Option<&Documentation> {
360 self.doc()
361 }
362
363 fn set_monotonicity(&self, data_type: &DataType) -> SetMonotonicity {
364 match data_type {
367 DataType::UInt8 => SetMonotonicity::Increasing,
368 DataType::UInt16 => SetMonotonicity::Increasing,
369 DataType::UInt32 => SetMonotonicity::Increasing,
370 DataType::UInt64 => SetMonotonicity::Increasing,
371 _ => SetMonotonicity::NotMonotonic,
372 }
373 }
374
375 fn simplify_expr_op_literal(
380 &self,
381 agg_function: &AggregateFunction,
382 arg: &Expr,
383 op: Operator,
384 lit: &Expr,
385 _arg_is_left: bool,
387 ) -> Result<Option<Expr>> {
388 if op != Operator::Plus {
389 return Ok(None);
390 }
391
392 let lit_type = match &lit {
393 Expr::Literal(value, _) => value.data_type(),
394 _ => {
395 return internal_err!(
396 "Sum::simplify_expr_op_literal got a non literal argument"
397 );
398 }
399 };
400 if lit_type == DataType::Null {
401 return Ok(None);
402 }
403
404 let mut sum_agg = agg_function.clone();
406 sum_agg.params.args = vec![arg.clone()];
407 let sum_agg = Expr::AggregateFunction(sum_agg);
408
409 let count_agg = cast(crate::count::count(arg.clone()), lit_type);
411
412 Ok(Some(sum_agg + (lit.clone() * count_agg)))
414 }
415
416 fn value_from_stats(&self, statistics_args: &StatisticsArgs) -> Option<ScalarValue> {
417 if statistics_args.is_distinct {
418 return None;
419 }
420
421 let [expr] = statistics_args.exprs else {
422 return None;
423 };
424
425 let (col_expr, cast_type) = match expr.downcast_ref::<Column>() {
426 Some(col_expr) => (col_expr, None),
427 None => {
428 let cast_expr = expr.downcast_ref::<CastExpr>()?;
429 let col_expr = cast_expr.expr().downcast_ref::<Column>()?;
430 (col_expr, Some(cast_expr.cast_type()))
431 }
432 };
433
434 let col_stats = statistics_args
435 .statistics
436 .column_statistics
437 .get(col_expr.index())?;
438
439 let Precision::Exact(val) = col_stats.sum_value.cast_to_sum_type() else {
443 return None;
444 };
445 if val.is_null() {
446 return None;
447 }
448
449 if let Some(cast_type) = cast_type {
454 let value_type = val.data_type();
455 if cast_type != statistics_args.return_type || &value_type != cast_type {
456 return None;
457 }
458 return Some(val);
459 }
460
461 if &val.data_type() == statistics_args.return_type {
462 Some(val)
463 } else {
464 val.cast_to(statistics_args.return_type).ok()
465 }
466 }
467}
468
469struct SumAccumulator<T: ArrowNumericType> {
471 sum: Option<T::Native>,
472 data_type: DataType,
473}
474
475impl<T: ArrowNumericType> std::fmt::Debug for SumAccumulator<T> {
476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
477 write!(f, "SumAccumulator({})", self.data_type)
478 }
479}
480
481impl<T: ArrowNumericType> SumAccumulator<T> {
482 fn new(data_type: DataType) -> Self {
483 Self {
484 sum: None,
485 data_type,
486 }
487 }
488}
489
490impl<T: ArrowNumericType> Accumulator for SumAccumulator<T> {
491 fn state(&mut self) -> Result<Vec<ScalarValue>> {
492 Ok(vec![self.evaluate()?])
493 }
494
495 fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
496 let values = values[0].as_primitive::<T>();
497 if let Some(x) = arrow::compute::sum(values) {
498 let v = self.sum.get_or_insert_with(|| T::Native::usize_as(0));
499 *v = v.add_wrapping(x);
500 }
501 Ok(())
502 }
503
504 fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
505 self.update_batch(states)
506 }
507
508 fn evaluate(&mut self) -> Result<ScalarValue> {
509 ScalarValue::new_primitive::<T>(self.sum, &self.data_type)
510 }
511
512 fn size(&self) -> usize {
513 size_of_val(self)
514 }
515}
516
517struct SlidingSumAccumulator<T: ArrowNumericType> {
521 sum: T::Native,
522 count: u64,
523 data_type: DataType,
524}
525
526impl<T: ArrowNumericType> std::fmt::Debug for SlidingSumAccumulator<T> {
527 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
528 write!(f, "SlidingSumAccumulator({})", self.data_type)
529 }
530}
531
532impl<T: ArrowNumericType> SlidingSumAccumulator<T> {
533 fn new(data_type: DataType) -> Self {
534 Self {
535 sum: T::Native::usize_as(0),
536 count: 0,
537 data_type,
538 }
539 }
540}
541
542impl<T: ArrowNumericType> Accumulator for SlidingSumAccumulator<T> {
543 fn state(&mut self) -> Result<Vec<ScalarValue>> {
544 Ok(vec![self.evaluate()?, self.count.into()])
545 }
546
547 fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
548 let values = values[0].as_primitive::<T>();
549 self.count += (values.len() - values.null_count()) as u64;
550 if let Some(x) = arrow::compute::sum(values) {
551 self.sum = self.sum.add_wrapping(x)
552 }
553 Ok(())
554 }
555
556 fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
557 let values = states[0].as_primitive::<T>();
558 if let Some(x) = arrow::compute::sum(values) {
559 self.sum = self.sum.add_wrapping(x)
560 }
561 if let Some(x) = arrow::compute::sum(states[1].as_primitive::<UInt64Type>()) {
562 self.count += x;
563 }
564 Ok(())
565 }
566
567 fn evaluate(&mut self) -> Result<ScalarValue> {
568 let v = (self.count != 0).then_some(self.sum);
569 ScalarValue::new_primitive::<T>(v, &self.data_type)
570 }
571
572 fn size(&self) -> usize {
573 size_of_val(self)
574 }
575
576 fn retract_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
577 let values = values[0].as_primitive::<T>();
578 if let Some(x) = arrow::compute::sum(values) {
579 self.sum = self.sum.sub_wrapping(x)
580 }
581 self.count -= (values.len() - values.null_count()) as u64;
582 Ok(())
583 }
584
585 fn supports_retract_batch(&self) -> bool {
586 true
587 }
588}
589
590#[derive(Debug)]
593pub struct SlidingDistinctSumAccumulator {
594 counts: HashMap<i64, usize, RandomState>,
596 sum: i64,
598 data_type: DataType,
600}
601
602impl SlidingDistinctSumAccumulator {
603 pub fn try_new(data_type: &DataType) -> Result<Self> {
605 if *data_type != DataType::Int64 {
607 return exec_err!(
608 "SlidingDistinctSumAccumulator only supports Int64, got {data_type}"
609 );
610 }
611 Ok(Self {
612 counts: HashMap::default(),
613 sum: 0,
614 data_type: data_type.clone(),
615 })
616 }
617
618 fn update_value(&mut self, value: i64) {
619 let cnt = self.counts.entry(value).or_insert(0);
620 if *cnt == 0 {
621 self.sum = self.sum.wrapping_add(value);
623 }
624 *cnt += 1;
625 }
626
627 fn retract_value(&mut self, value: i64) {
628 if let Some(cnt) = self.counts.get_mut(&value) {
629 *cnt -= 1;
630 if *cnt == 0 {
631 self.sum = self.sum.wrapping_sub(value);
633 self.counts.remove(&value);
634 }
635 }
636 }
637
638 fn apply_valid_values<F>(
639 &mut self,
640 arr: &arrow::array::PrimitiveArray<Int64Type>,
641 mut op: F,
642 ) where
643 F: FnMut(&mut Self, i64),
644 {
645 if arr.null_count() == 0 {
646 for &value in arr.values() {
647 op(self, value);
648 }
649 } else {
650 for (idx, &value) in arr.values().iter().enumerate() {
651 if arr.is_valid(idx) {
652 op(self, value);
653 }
654 }
655 }
656 }
657}
658
659impl Accumulator for SlidingDistinctSumAccumulator {
660 fn update_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
661 let arr = values[0].as_primitive::<Int64Type>();
662 self.apply_valid_values(arr, Self::update_value);
663 Ok(())
664 }
665
666 fn evaluate(&mut self) -> Result<ScalarValue> {
667 Ok(ScalarValue::Int64(
669 (!self.counts.is_empty()).then_some(self.sum),
670 ))
671 }
672
673 fn size(&self) -> usize {
674 size_of_val(self) + self.counts.capacity() * size_of::<(i64, usize)>()
676 }
677
678 fn state(&mut self) -> Result<Vec<ScalarValue>> {
679 let keys = self
681 .counts
682 .keys()
683 .cloned()
684 .map(Some)
685 .map(ScalarValue::Int64)
686 .collect::<Vec<_>>();
687 Ok(vec![ScalarValue::List(ScalarValue::new_list_nullable(
688 &keys,
689 &self.data_type,
690 ))])
691 }
692
693 fn merge_batch(&mut self, states: &[ArrayRef]) -> Result<()> {
694 let list_arr = states[0].as_list::<i32>();
696 for maybe_inner in list_arr.iter().flatten() {
697 for idx in 0..maybe_inner.len() {
698 if let ScalarValue::Int64(Some(v)) =
699 ScalarValue::try_from_array(&*maybe_inner, idx)?
700 {
701 self.update_value(v);
702 }
703 }
704 }
705 Ok(())
706 }
707
708 fn retract_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
709 let arr = values[0].as_primitive::<Int64Type>();
710 self.apply_valid_values(arr, Self::retract_value);
711 Ok(())
712 }
713
714 fn supports_retract_batch(&self) -> bool {
715 true
716 }
717}
718
719#[cfg(test)]
720mod tests {
721 use super::*;
722 use arrow::{
723 array::{Decimal128Array, Int64Array},
724 buffer::{NullBuffer, ScalarBuffer},
725 };
726 use std::{
727 mem::{size_of, size_of_val},
728 sync::Arc,
729 };
730
731 #[test]
732 fn sliding_distinct_sum_ignores_null_slots() -> Result<()> {
733 let mut acc = SlidingDistinctSumAccumulator::try_new(&DataType::Int64)?;
734
735 let values: ArrayRef = Arc::new(Int64Array::new(
736 ScalarBuffer::from(vec![42, 5, 5]),
737 Some(NullBuffer::from(vec![false, true, true])),
738 ));
739 acc.update_batch(&[values])?;
740 assert_eq!(acc.evaluate()?, ScalarValue::Int64(Some(5)));
741
742 let retract: ArrayRef = Arc::new(Int64Array::new(
743 ScalarBuffer::from(vec![42, 5]),
744 Some(NullBuffer::from(vec![false, true])),
745 ));
746 acc.retract_batch(&[retract])?;
747 assert_eq!(acc.evaluate()?, ScalarValue::Int64(Some(5)));
748
749 let retract_last: ArrayRef =
750 Arc::new(Int64Array::new(ScalarBuffer::from(vec![5]), None));
751 acc.retract_batch(&[retract_last])?;
752 assert_eq!(acc.evaluate()?, ScalarValue::Int64(None));
753
754 Ok(())
755 }
756
757 fn expected_sliding_distinct_sum_size(acc: &SlidingDistinctSumAccumulator) -> usize {
758 size_of_val(acc) + acc.counts.capacity() * size_of::<(i64, usize)>()
759 }
760
761 #[test]
762 fn sliding_distinct_sum_size_includes_hash_map_capacity() -> Result<()> {
763 let mut acc = SlidingDistinctSumAccumulator::try_new(&DataType::Int64)?;
764 let empty_size = acc.size();
765 let values: ArrayRef = Arc::new(Int64Array::from(vec![1, 2, 3]));
766 acc.update_batch(&[Arc::clone(&values)])?;
767
768 let expected = expected_sliding_distinct_sum_size(&acc);
769 assert!(acc.counts.capacity() > 0);
770 assert_eq!(acc.size(), expected);
771 assert!(acc.size() > empty_size);
772
773 let initial_capacity = acc.counts.capacity();
774 let additional_values: ArrayRef =
775 Arc::new(Int64Array::from_iter(4..4 + initial_capacity as i64 + 1));
776 acc.update_batch(&[Arc::clone(&additional_values)])?;
777
778 let grown_size = expected_sliding_distinct_sum_size(&acc);
779 assert!(acc.counts.capacity() > initial_capacity);
780 assert_eq!(acc.size(), grown_size);
781 assert!(acc.size() > expected);
782
783 acc.retract_batch(&[values])?;
784 acc.retract_batch(&[additional_values])?;
785 assert!(acc.counts.is_empty());
786 assert_eq!(acc.size(), grown_size);
787
788 Ok(())
789 }
790
791 #[test]
792 fn sliding_distinct_sum_returns_null_for_all_null_frame() -> Result<()> {
793 let mut acc = SlidingDistinctSumAccumulator::try_new(&DataType::Int64)?;
794
795 let values: ArrayRef = Arc::new(Int64Array::new(
796 ScalarBuffer::from(vec![99]),
797 Some(NullBuffer::from(vec![false])),
798 ));
799 acc.update_batch(&[values])?;
800 assert_eq!(acc.evaluate()?, ScalarValue::Int64(None));
801
802 Ok(())
803 }
804
805 #[test]
806 fn decimal_sum_accumulator_uses_widened_return_type() -> Result<()> {
807 let values: ArrayRef = Arc::new(
808 Decimal128Array::from(vec![Some(99_999), Some(99_999)])
809 .with_precision_and_scale(5, 2)?,
810 );
811 let mut acc = SumAccumulator::<Decimal128Type>::new(DataType::Decimal128(15, 2));
812
813 acc.update_batch(&[values])?;
814
815 assert_eq!(
816 acc.evaluate()?,
817 ScalarValue::Decimal128(Some(199_998), 15, 2)
818 );
819 Ok(())
820 }
821
822 #[test]
823 fn sum_value_from_stats_widens_small_integer_sum() {
824 let statistics = datafusion_common::Statistics {
825 num_rows: Precision::Absent,
826 total_byte_size: Precision::Absent,
827 column_statistics: vec![datafusion_common::ColumnStatistics {
828 sum_value: Precision::Exact(ScalarValue::Int32(Some(10))),
829 ..Default::default()
830 }],
831 };
832 let return_type = DataType::Int64;
833 let expr: Arc<dyn datafusion_physical_expr::PhysicalExpr> =
834 Arc::new(Column::new("a", 0));
835 let exprs = vec![expr];
836 let statistics_args = StatisticsArgs {
837 statistics: &statistics,
838 return_type: &return_type,
839 is_distinct: false,
840 exprs: &exprs,
841 };
842
843 assert_eq!(
844 Sum::new().value_from_stats(&statistics_args),
845 Some(ScalarValue::Int64(Some(10)))
846 );
847 }
848
849 #[test]
850 fn sum_value_from_stats_casts_decimal_sum_to_return_type() {
851 let statistics = datafusion_common::Statistics {
852 num_rows: Precision::Absent,
853 total_byte_size: Precision::Absent,
854 column_statistics: vec![datafusion_common::ColumnStatistics {
855 sum_value: Precision::Exact(ScalarValue::Decimal128(Some(12345), 5, 2)),
856 ..Default::default()
857 }],
858 };
859 let return_type = DataType::Decimal128(15, 2);
860 let expr: Arc<dyn datafusion_physical_expr::PhysicalExpr> =
861 Arc::new(Column::new("a", 0));
862 let exprs = vec![expr];
863 let statistics_args = StatisticsArgs {
864 statistics: &statistics,
865 return_type: &return_type,
866 is_distinct: false,
867 exprs: &exprs,
868 };
869
870 assert_eq!(
871 Sum::new().value_from_stats(&statistics_args),
872 Some(ScalarValue::Decimal128(Some(12345), 15, 2))
873 );
874 }
875}