@@ -23,12 +23,14 @@ use std::mem::{size_of, size_of_val};
2323use std:: sync:: Arc ;
2424
2525use arrow:: array:: {
26- new_empty_array, Array , ArrayRef , AsArray , BooleanArray , ListArray , StructArray ,
26+ make_array, new_empty_array, Array , ArrayRef , AsArray , BooleanArray , ListArray ,
27+ StructArray ,
2728} ;
2829use arrow:: compute:: { filter, SortOptions } ;
2930use arrow:: datatypes:: { DataType , Field , FieldRef , Fields } ;
3031
3132use datafusion_common:: cast:: as_list_array;
33+ use datafusion_common:: scalar:: copy_array_data;
3234use datafusion_common:: utils:: { get_row_at_idx, SingleRowListArrayBuilder } ;
3335use datafusion_common:: { exec_err, internal_err, Result , ScalarValue } ;
3436use datafusion_expr:: function:: { AccumulatorArgs , StateFieldsArgs } ;
@@ -313,7 +315,11 @@ impl Accumulator for ArrayAggAccumulator {
313315 } ;
314316
315317 if !val. is_empty ( ) {
316- self . values . push ( val) ;
318+ // The ArrayRef might be holding a reference to its original input buffer, so
319+ // storing it here directly copied/compacted avoids over accounting memory
320+ // not used here.
321+ self . values
322+ . push ( make_array ( copy_array_data ( & val. to_data ( ) ) ) ) ;
317323 }
318324
319325 Ok ( ( ) )
@@ -423,7 +429,8 @@ impl Accumulator for DistinctArrayAggAccumulator {
423429 if nulls. is_none_or ( |nulls| nulls. null_count ( ) < val. len ( ) ) {
424430 for i in 0 ..val. len ( ) {
425431 if nulls. is_none_or ( |nulls| nulls. is_valid ( i) ) {
426- self . values . insert ( ScalarValue :: try_from_array ( val, i) ?) ;
432+ self . values
433+ . insert ( ScalarValue :: try_from_array ( val, i) ?. compacted ( ) ) ;
427434 }
428435 }
429436 }
@@ -577,8 +584,14 @@ impl Accumulator for OrderSensitiveArrayAggAccumulator {
577584 if nulls. is_none_or ( |nulls| nulls. null_count ( ) < val. len ( ) ) {
578585 for i in 0 ..val. len ( ) {
579586 if nulls. is_none_or ( |nulls| nulls. is_valid ( i) ) {
580- self . values . push ( ScalarValue :: try_from_array ( val, i) ?) ;
581- self . ordering_values . push ( get_row_at_idx ( ord, i) ?)
587+ self . values
588+ . push ( ScalarValue :: try_from_array ( val, i) ?. compacted ( ) ) ;
589+ self . ordering_values . push (
590+ get_row_at_idx ( ord, i) ?
591+ . into_iter ( )
592+ . map ( |v| v. compacted ( ) )
593+ . collect ( ) ,
594+ )
582595 }
583596 }
584597 }
@@ -714,6 +727,7 @@ impl Accumulator for OrderSensitiveArrayAggAccumulator {
714727#[ cfg( test) ]
715728mod tests {
716729 use super :: * ;
730+ use arrow:: array:: { ListBuilder , StringBuilder } ;
717731 use arrow:: datatypes:: { FieldRef , Schema } ;
718732 use datafusion_common:: cast:: as_generic_string_array;
719733 use datafusion_common:: internal_err;
@@ -980,6 +994,56 @@ mod tests {
980994 Ok ( ( ) )
981995 }
982996
997+ #[ test]
998+ fn does_not_over_account_memory ( ) -> Result < ( ) > {
999+ let ( mut acc1, mut acc2) = ArrayAggAccumulatorBuilder :: string ( ) . build_two ( ) ?;
1000+
1001+ acc1. update_batch ( & [ data ( [ "a" , "c" , "b" ] ) ] ) ?;
1002+ acc2. update_batch ( & [ data ( [ "b" , "c" , "a" ] ) ] ) ?;
1003+ acc1 = merge ( acc1, acc2) ?;
1004+
1005+ // without compaction, the size is 2652.
1006+ assert_eq ! ( acc1. size( ) , 732 ) ;
1007+
1008+ Ok ( ( ) )
1009+ }
1010+ #[ test]
1011+ fn does_not_over_account_memory_distinct ( ) -> Result < ( ) > {
1012+ let ( mut acc1, mut acc2) = ArrayAggAccumulatorBuilder :: string ( )
1013+ . distinct ( )
1014+ . build_two ( ) ?;
1015+
1016+ acc1. update_batch ( & [ string_list_data ( [
1017+ vec ! [ "a" , "b" , "c" ] ,
1018+ vec ! [ "d" , "e" , "f" ] ,
1019+ ] ) ] ) ?;
1020+ acc2. update_batch ( & [ string_list_data ( [ vec ! [ "e" , "f" , "g" ] ] ) ] ) ?;
1021+ acc1 = merge ( acc1, acc2) ?;
1022+
1023+ // without compaction, the size is 16660
1024+ assert_eq ! ( acc1. size( ) , 1660 ) ;
1025+
1026+ Ok ( ( ) )
1027+ }
1028+
1029+ #[ test]
1030+ fn does_not_over_account_memory_ordered ( ) -> Result < ( ) > {
1031+ let mut acc = ArrayAggAccumulatorBuilder :: string ( )
1032+ . order_by_col ( "col" , SortOptions :: new ( false , false ) )
1033+ . build ( ) ?;
1034+
1035+ acc. update_batch ( & [ string_list_data ( [
1036+ vec ! [ "a" , "b" , "c" ] ,
1037+ vec ! [ "c" , "d" , "e" ] ,
1038+ vec ! [ "b" , "c" , "d" ] ,
1039+ ] ) ] ) ?;
1040+
1041+ // without compaction, the size is 17112
1042+ assert_eq ! ( acc. size( ) , 2112 ) ;
1043+
1044+ Ok ( ( ) )
1045+ }
1046+
9831047 struct ArrayAggAccumulatorBuilder {
9841048 return_field : FieldRef ,
9851049 distinct : bool ,
@@ -1059,6 +1123,15 @@ mod tests {
10591123 . collect ( )
10601124 }
10611125
1126+ fn string_list_data < ' a > ( data : impl IntoIterator < Item = Vec < & ' a str > > ) -> ArrayRef {
1127+ let mut builder = ListBuilder :: new ( StringBuilder :: new ( ) ) ;
1128+ for string_list in data. into_iter ( ) {
1129+ builder. append_value ( string_list. iter ( ) . map ( Some ) . collect :: < Vec < _ > > ( ) ) ;
1130+ }
1131+
1132+ Arc :: new ( builder. finish ( ) )
1133+ }
1134+
10621135 fn data < T , const N : usize > ( list : [ T ; N ] ) -> ArrayRef
10631136 where
10641137 ScalarValue : From < T > ,
0 commit comments