@@ -519,9 +519,16 @@ impl Accumulator for AvgAccumulator {
519519 }
520520
521521 fn evaluate ( & mut self ) -> Result < ScalarValue > {
522- Ok ( ScalarValue :: Float64 (
523- self . sum . map ( |f| f / self . count as f64 ) ,
524- ) )
522+ // In sliding-window mode `retract_batch` can bring `count` back to 0
523+ // while `sum` remains `Some(..)` (possibly zero or a floating-point
524+ // residual). Guard against that so the frame with no non-NULL values
525+ // yields NULL rather than NaN / ±Inf.
526+ let avg = if self . count == 0 {
527+ None
528+ } else {
529+ self . sum . map ( |f| f / self . count as f64 )
530+ } ;
531+ Ok ( ScalarValue :: Float64 ( avg) )
525532 }
526533
527534 fn size ( & self ) -> usize {
@@ -584,17 +591,23 @@ impl<T: DecimalType + ArrowNumericType + Debug> Accumulator for DecimalAvgAccumu
584591 }
585592
586593 fn evaluate ( & mut self ) -> Result < ScalarValue > {
587- let v = self
588- . sum
589- . map ( |v| {
590- DecimalAverager :: < T > :: try_new (
591- self . sum_scale ,
592- self . target_precision ,
593- self . target_scale ,
594- ) ?
595- . avg ( v, T :: Native :: from_usize ( self . count as usize ) . unwrap ( ) )
596- } )
597- . transpose ( ) ?;
594+ // `count == 0` can occur in sliding-window mode after `retract_batch`
595+ // removes every contributing value. Return NULL rather than dividing
596+ // by zero (which would panic for integer decimal types).
597+ let v = if self . count == 0 {
598+ None
599+ } else {
600+ self . sum
601+ . map ( |v| {
602+ DecimalAverager :: < T > :: try_new (
603+ self . sum_scale ,
604+ self . target_precision ,
605+ self . target_scale ,
606+ ) ?
607+ . avg ( v, T :: Native :: from_usize ( self . count as usize ) . unwrap ( ) )
608+ } )
609+ . transpose ( ) ?
610+ } ;
598611
599612 ScalarValue :: new_primitive :: < T > (
600613 v,
@@ -670,7 +683,14 @@ impl Accumulator for DurationAvgAccumulator {
670683 }
671684
672685 fn evaluate ( & mut self ) -> Result < ScalarValue > {
673- let avg = self . sum . map ( |sum| sum / self . count as i64 ) ;
686+ // Guard against `count == 0` which can happen in sliding-window mode
687+ // after every contributing value has been retracted. Without this
688+ // check we would integer-divide by zero.
689+ let avg = if self . count == 0 {
690+ None
691+ } else {
692+ self . sum . map ( |sum| sum / self . count as i64 )
693+ } ;
674694
675695 match self . result_unit {
676696 TimeUnit :: Second => Ok ( ScalarValue :: DurationSecond ( avg) ) ,
0 commit comments