@@ -12,9 +12,9 @@ use std::fmt::Formatter;
1212
1313pub ( crate ) use grouped:: PrimitiveGroupedSumEncodingKernel ;
1414use prost:: Message ;
15- use vortex_error:: VortexExpect ;
1615use vortex_error:: VortexResult ;
1716use vortex_error:: vortex_bail;
17+ use vortex_error:: vortex_ensure;
1818use vortex_error:: vortex_err;
1919use vortex_error:: vortex_panic;
2020use vortex_proto:: expr as pb;
@@ -250,85 +250,31 @@ impl AggregateFnVTable for Sum {
250250 }
251251
252252 fn combine_partials ( & self , partial : & mut Self :: Partial , other : Scalar ) -> VortexResult < ( ) > {
253- let other = other . cast ( & sum_partial_dtype ( partial . return_dtype . clone ( ) ) ) ?;
254- if other . is_null ( ) {
255- partial . is_empty = false ;
256- partial. is_overflow = true ;
253+ let ( other_sum , other_is_overflow , other_is_empty ) = decode_sum_partial_scalar ( other ) ?;
254+ validate_sum_field_dtype ( & other_sum , & partial . return_dtype ) ? ;
255+
256+ if partial. is_overflow {
257257 return Ok ( ( ) ) ;
258258 }
259-
260- let fields = other. as_struct ( ) ;
261- let other = fields
262- . field ( SUM_FIELD )
263- . ok_or_else ( || vortex_err ! ( "Sum partial is missing the `{SUM_FIELD}` field" ) ) ?;
264- let other_is_overflow = fields
265- . field ( IS_OVERFLOW_FIELD )
266- . and_then ( |is_overflow| is_overflow. as_bool ( ) . value ( ) )
267- . ok_or_else ( || vortex_err ! ( "Sum partial has an invalid `{IS_OVERFLOW_FIELD}` field" ) ) ?;
268- let other_is_empty = fields
269- . field ( IS_EMPTY_FIELD )
270- . and_then ( |is_empty| is_empty. as_bool ( ) . value ( ) )
271- . ok_or_else ( || vortex_err ! ( "Sum partial has an invalid `{IS_EMPTY_FIELD}` field" ) ) ?;
272-
273- partial. is_empty &= other_is_empty;
274- if partial. is_overflow || other_is_overflow {
259+ if other_is_overflow {
275260 partial. is_overflow = true ;
261+ partial. is_empty = false ;
276262 return Ok ( ( ) ) ;
277263 }
278264 if other_is_empty {
279265 return Ok ( ( ) ) ;
280266 }
281267
282- let saturated = match & mut partial. sum {
283- SumState :: Unsigned ( acc) => {
284- let val = other
285- . as_primitive ( )
286- . typed_value :: < u64 > ( )
287- . vortex_expect ( "checked non-null" ) ;
288- checked_add_u64 ( acc, val)
289- }
290- SumState :: Signed ( acc) => {
291- let val = other
292- . as_primitive ( )
293- . typed_value :: < i64 > ( )
294- . vortex_expect ( "checked non-null" ) ;
295- checked_add_i64 ( acc, val)
296- }
297- SumState :: Float ( acc) => {
298- let val = other
299- . as_primitive ( )
300- . typed_value :: < f64 > ( )
301- . vortex_expect ( "checked non-null" ) ;
302- * acc += val;
303- false
304- }
305- SumState :: Decimal { value, dtype } => {
306- let val = other
307- . as_decimal ( )
308- . decimal_value ( )
309- . vortex_expect ( "checked non-null" ) ;
310- match value. checked_add ( & val) {
311- Some ( r) if r. fits_in_precision ( * dtype) => {
312- * value = r;
313- false
314- }
315- Some ( _) | None => true ,
316- }
317- }
318- } ;
319- if saturated {
320- partial. is_overflow = true ;
321- } else {
322- partial. is_empty = false ;
323- }
268+ partial. is_overflow = checked_add_sum_state ( & mut partial. sum , & other_sum) ?;
269+ partial. is_empty = false ;
324270 Ok ( ( ) )
325271 }
326272
327273 fn to_scalar ( & self , partial : & Self :: Partial ) -> VortexResult < Scalar > {
328274 Ok ( Scalar :: struct_ (
329275 sum_partial_dtype ( partial. return_dtype . clone ( ) ) ,
330276 vec ! [
331- sum_state_scalar( partial) ,
277+ sum_state_scalar( partial, Nullability :: NonNullable ) ,
332278 Scalar :: bool ( partial. is_overflow, Nullability :: NonNullable ) ,
333279 Scalar :: bool ( partial. is_empty, Nullability :: NonNullable ) ,
334280 ] ,
@@ -481,6 +427,60 @@ pub enum SumState {
481427 } ,
482428}
483429
430+ fn decode_sum_partial_scalar ( scalar : Scalar ) -> VortexResult < ( Scalar , bool , bool ) > {
431+ vortex_ensure ! ( !scalar. is_null( ) , "Sum partial must not be null" ) ;
432+
433+ let Some ( fields) = scalar. as_struct_opt ( ) else {
434+ vortex_bail ! ( "Sum partial must be a struct, got {}" , scalar. dtype( ) ) ;
435+ } ;
436+ let sum = fields
437+ . field ( SUM_FIELD )
438+ . ok_or_else ( || vortex_err ! ( "Sum partial is missing the `{SUM_FIELD}` field" ) ) ?;
439+ let is_overflow =
440+ bool:: try_from ( & fields. field ( IS_OVERFLOW_FIELD ) . ok_or_else ( || {
441+ vortex_err ! ( "Sum partial is missing the `{IS_OVERFLOW_FIELD}` field" )
442+ } ) ?) ?;
443+ let is_empty = bool:: try_from (
444+ & fields
445+ . field ( IS_EMPTY_FIELD )
446+ . ok_or_else ( || vortex_err ! ( "Sum partial is missing the `{IS_EMPTY_FIELD}` field" ) ) ?,
447+ ) ?;
448+
449+ Ok ( ( sum, is_overflow, is_empty) )
450+ }
451+
452+ fn validate_sum_field_dtype ( sum : & Scalar , return_dtype : & DType ) -> VortexResult < ( ) > {
453+ vortex_ensure ! (
454+ sum. dtype( ) . nullability( ) == Nullability :: NonNullable
455+ && sum. dtype( ) . eq_ignore_nullability( return_dtype) ,
456+ "Sum partial value has dtype {}, expected {}" ,
457+ sum. dtype( ) ,
458+ return_dtype. as_nonnullable( ) ,
459+ ) ;
460+ Ok ( ( ) )
461+ }
462+
463+ fn checked_add_sum_state ( state : & mut SumState , other : & Scalar ) -> VortexResult < bool > {
464+ Ok ( match state {
465+ SumState :: Unsigned ( acc) => checked_add_u64 ( acc, u64:: try_from ( other) ?) ,
466+ SumState :: Signed ( acc) => checked_add_i64 ( acc, i64:: try_from ( other) ?) ,
467+ SumState :: Float ( acc) => {
468+ * acc += f64:: try_from ( other) ?;
469+ false
470+ }
471+ SumState :: Decimal { value, dtype } => {
472+ let other = DecimalValue :: try_from ( other) ?;
473+ match value. checked_add ( & other) {
474+ Some ( result) if result. fits_in_precision ( * dtype) => {
475+ * value = result;
476+ false
477+ }
478+ Some ( _) | None => true ,
479+ }
480+ }
481+ } )
482+ }
483+
484484fn make_zero_state ( return_dtype : & DType ) -> SumState {
485485 match return_dtype {
486486 DType :: Primitive ( ptype, _) => match ptype {
@@ -497,35 +497,30 @@ fn make_zero_state(return_dtype: &DType) -> SumState {
497497}
498498
499499fn sum_partial_dtype ( sum_dtype : DType ) -> DType {
500- DType :: Struct (
501- StructFields :: new (
502- FieldNames :: from_iter ( [
503- FieldName :: from ( SUM_FIELD ) ,
504- FieldName :: from ( IS_OVERFLOW_FIELD ) ,
505- FieldName :: from ( IS_EMPTY_FIELD ) ,
506- ] ) ,
507- vec ! [
508- sum_dtype. as_nonnullable( ) ,
509- DType :: Bool ( Nullability :: NonNullable ) ,
510- DType :: Bool ( Nullability :: NonNullable ) ,
511- ] ,
512- ) ,
513- Nullability :: Nullable ,
500+ DType :: Struct ( sum_partial_fields ( sum_dtype) , Nullability :: Nullable )
501+ }
502+
503+ fn sum_partial_fields ( sum_dtype : DType ) -> StructFields {
504+ StructFields :: new (
505+ FieldNames :: from_iter ( [
506+ FieldName :: from ( SUM_FIELD ) ,
507+ FieldName :: from ( IS_OVERFLOW_FIELD ) ,
508+ FieldName :: from ( IS_EMPTY_FIELD ) ,
509+ ] ) ,
510+ vec ! [
511+ sum_dtype. as_nonnullable( ) ,
512+ DType :: Bool ( Nullability :: NonNullable ) ,
513+ DType :: Bool ( Nullability :: NonNullable ) ,
514+ ] ,
514515 )
515516}
516517
517- fn sum_state_scalar ( partial : & SumPartial ) -> Scalar {
518+ fn sum_state_scalar ( partial : & SumPartial , nullability : Nullability ) -> Scalar {
518519 match & partial. sum {
519- SumState :: Unsigned ( v) => Scalar :: primitive ( * v, Nullability :: NonNullable ) ,
520- SumState :: Signed ( v) => Scalar :: primitive ( * v, Nullability :: NonNullable ) ,
521- SumState :: Float ( v) => Scalar :: primitive ( * v, Nullability :: NonNullable ) ,
522- SumState :: Decimal { value, .. } => {
523- let decimal_dtype = * partial
524- . return_dtype
525- . as_decimal_opt ( )
526- . vortex_expect ( "return dtype must be decimal" ) ;
527- Scalar :: decimal ( * value, decimal_dtype, Nullability :: NonNullable )
528- }
520+ SumState :: Unsigned ( v) => Scalar :: primitive ( * v, nullability) ,
521+ SumState :: Signed ( v) => Scalar :: primitive ( * v, nullability) ,
522+ SumState :: Float ( v) => Scalar :: primitive ( * v, nullability) ,
523+ SumState :: Decimal { value, dtype } => Scalar :: decimal ( * value, * dtype, nullability) ,
529524 }
530525}
531526
@@ -534,22 +529,7 @@ fn sum_value_scalar(partial: &SumPartial) -> Scalar {
534529 return Scalar :: null ( partial. return_dtype . as_nullable ( ) ) ;
535530 }
536531
537- nullable_sum_state_scalar ( partial)
538- }
539-
540- fn nullable_sum_state_scalar ( partial : & SumPartial ) -> Scalar {
541- match & partial. sum {
542- SumState :: Unsigned ( v) => Scalar :: primitive ( * v, Nullability :: Nullable ) ,
543- SumState :: Signed ( v) => Scalar :: primitive ( * v, Nullability :: Nullable ) ,
544- SumState :: Float ( v) => Scalar :: primitive ( * v, Nullability :: Nullable ) ,
545- SumState :: Decimal { value, .. } => {
546- let decimal_dtype = * partial
547- . return_dtype
548- . as_decimal_opt ( )
549- . vortex_expect ( "return dtype must be decimal" ) ;
550- Scalar :: decimal ( * value, decimal_dtype, Nullability :: Nullable )
551- }
552- }
532+ sum_state_scalar ( partial, Nullability :: Nullable )
553533}
554534
555535/// Convert scalar legacy Sum partials read from storage (a single nullable primitive) to the struct partial shape.
0 commit comments