• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vortex-data / vortex / 16935267080

13 Aug 2025 11:00AM UTC coverage: 24.312% (-63.3%) from 87.658%
16935267080

Pull #4226

github

web-flow
Merge 81b48c7fb into baa6ea202
Pull Request #4226: Support converting TimestampTZ to and from duckdb

0 of 2 new or added lines in 1 file covered. (0.0%)

20666 existing lines in 469 files now uncovered.

8726 of 35892 relevant lines covered (24.31%)

147.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.57
/vortex-array/src/compute/sum.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::sync::LazyLock;
5

6
use arcref::ArcRef;
7
use vortex_dtype::DType;
8
use vortex_error::{VortexResult, vortex_err, vortex_panic};
9
use vortex_scalar::Scalar;
10

11
use crate::Array;
12
use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Output, UnaryArgs};
13
use crate::stats::{Precision, Stat, StatsProvider};
14
use crate::vtable::VTable;
15

16
static SUM_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
2✔
17
    let compute = ComputeFn::new("sum".into(), ArcRef::new_ref(&Sum));
2✔
18
    for kernel in inventory::iter::<SumKernelRef> {
14✔
19
        compute.register_kernel(kernel.0.clone());
12✔
20
    }
12✔
21
    compute
2✔
22
});
2✔
23

24
/// Sum an array.
25
///
26
/// If the sum overflows, a null scalar will be returned.
27
/// If the sum is not supported for the array's dtype, an error will be raised.
28
/// If the array is all-invalid, the sum will be zero.
29
pub fn sum(array: &dyn Array) -> VortexResult<Scalar> {
30✔
30
    SUM_FN
30✔
31
        .invoke(&InvocationArgs {
30✔
32
            inputs: &[array.into()],
30✔
33
            options: &(),
30✔
34
        })?
30✔
35
        .unwrap_scalar()
30✔
36
}
30✔
37

38
struct Sum;
39

40
impl ComputeFnVTable for Sum {
41
    fn invoke(
30✔
42
        &self,
30✔
43
        args: &InvocationArgs,
30✔
44
        kernels: &[ArcRef<dyn Kernel>],
30✔
45
    ) -> VortexResult<Output> {
30✔
46
        let UnaryArgs { array, .. } = UnaryArgs::<()>::try_from(args)?;
30✔
47

48
        // Compute the expected dtype of the sum.
49
        let sum_dtype = self.return_dtype(args)?;
30✔
50

51
        // Short-circuit using array statistics.
52
        if let Some(Precision::Exact(sum)) = array.statistics().get(Stat::Sum) {
30✔
UNCOV
53
            return Ok(sum.into());
×
54
        }
30✔
55

56
        let sum_scalar = sum_impl(array, sum_dtype, kernels)?;
30✔
57

58
        // Update the statistics with the computed sum.
59
        array
30✔
60
            .statistics()
30✔
61
            .set(Stat::Sum, Precision::Exact(sum_scalar.value().clone()));
30✔
62

63
        Ok(sum_scalar.into())
30✔
64
    }
30✔
65

66
    fn return_dtype(&self, args: &InvocationArgs) -> VortexResult<DType> {
60✔
67
        let UnaryArgs { array, .. } = UnaryArgs::<()>::try_from(args)?;
60✔
68
        Stat::Sum
60✔
69
            .dtype(array.dtype())
60✔
70
            .ok_or_else(|| vortex_err!("Sum not supported for dtype: {}", array.dtype()))
60✔
71
    }
60✔
72

73
    fn return_len(&self, _args: &InvocationArgs) -> VortexResult<usize> {
30✔
74
        // The sum function always returns a single scalar value.
75
        Ok(1)
30✔
76
    }
30✔
77

78
    fn is_elementwise(&self) -> bool {
30✔
79
        false
30✔
80
    }
30✔
81
}
82

83
pub struct SumKernelRef(ArcRef<dyn Kernel>);
84
inventory::collect!(SumKernelRef);
85

86
pub trait SumKernel: VTable {
87
    /// # Preconditions
88
    ///
89
    /// * The array's DType is summable
90
    /// * The array is not all-null
91
    fn sum(&self, array: &Self::Array) -> VortexResult<Scalar>;
92
}
93

94
#[derive(Debug)]
95
pub struct SumKernelAdapter<V: VTable>(pub V);
96

97
impl<V: VTable + SumKernel> SumKernelAdapter<V> {
98
    pub const fn lift(&'static self) -> SumKernelRef {
×
99
        SumKernelRef(ArcRef::new_ref(self))
×
100
    }
×
101
}
102

103
impl<V: VTable + SumKernel> Kernel for SumKernelAdapter<V> {
104
    fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
150✔
105
        let UnaryArgs { array, .. } = UnaryArgs::<()>::try_from(args)?;
150✔
106
        let Some(array) = array.as_opt::<V>() else {
150✔
107
            return Ok(None);
120✔
108
        };
109
        Ok(Some(V::sum(&self.0, array)?.into()))
30✔
110
    }
150✔
111
}
112

113
/// Sum an array.
114
///
115
/// If the sum overflows, a null scalar will be returned.
116
/// If the sum is not supported for the array's dtype, an error will be raised.
117
/// If the array is all-invalid, the sum will be zero.
118
pub fn sum_impl(
30✔
119
    array: &dyn Array,
30✔
120
    sum_dtype: DType,
30✔
121
    kernels: &[ArcRef<dyn Kernel>],
30✔
122
) -> VortexResult<Scalar> {
30✔
123
    if array.is_empty() {
30✔
UNCOV
124
        return if sum_dtype.is_float() {
×
UNCOV
125
            Ok(Scalar::new(sum_dtype, 0.0.into()))
×
126
        } else {
UNCOV
127
            Ok(Scalar::new(sum_dtype, 0.into()))
×
128
        };
129
    }
30✔
130

131
    // Sum of all null is null.
132
    if array.all_invalid()? {
30✔
UNCOV
133
        return Ok(Scalar::null(sum_dtype));
×
134
    }
30✔
135

136
    // Try to find a sum kernel
137
    let args = InvocationArgs {
30✔
138
        inputs: &[array.into()],
30✔
139
        options: &(),
30✔
140
    };
30✔
141
    for kernel in kernels {
150✔
142
        if let Some(output) = kernel.invoke(&args)? {
150✔
143
            return output.unwrap_scalar();
30✔
144
        }
120✔
145
    }
UNCOV
146
    if let Some(output) = array.invoke(&SUM_FN, &args)? {
×
147
        return output.unwrap_scalar();
×
UNCOV
148
    }
×
149

150
    // Otherwise, canonicalize and try again.
UNCOV
151
    log::debug!("No sum implementation found for {}", array.encoding_id());
×
UNCOV
152
    if array.is_canonical() {
×
153
        // Panic to avoid recursion, but it should never be hit.
154
        vortex_panic!(
×
155
            "No sum implementation found for canonical array: {}",
×
156
            array.encoding_id()
×
157
        );
UNCOV
158
    }
×
UNCOV
159
    sum(array.to_canonical()?.as_ref())
×
160
}
30✔
161

162
#[cfg(test)]
163
mod test {
164
    use vortex_dtype::{DType, Nullability, PType};
165
    use vortex_scalar::Scalar;
166

167
    use crate::arrays::{BoolArray, PrimitiveArray};
168
    use crate::compute::sum;
169

170
    #[test]
171
    fn sum_all_invalid() {
172
        let array = PrimitiveArray::from_option_iter::<i32, _>([None, None, None]);
173
        let result = sum(array.as_ref()).unwrap();
174
        assert_eq!(
175
            result,
176
            Scalar::null(DType::Primitive(PType::I64, Nullability::Nullable))
177
        );
178
    }
179

180
    #[test]
181
    fn sum_all_invalid_float() {
182
        let array = PrimitiveArray::from_option_iter::<f32, _>([None, None, None]);
183
        let result = sum(array.as_ref()).unwrap();
184
        assert_eq!(
185
            result,
186
            Scalar::null(DType::Primitive(PType::F64, Nullability::Nullable))
187
        );
188
    }
189

190
    #[test]
191
    fn sum_constant() {
192
        let array = PrimitiveArray::from_iter([1, 1, 1, 1]);
193
        let result = sum(array.as_ref()).unwrap();
194
        assert_eq!(result.as_primitive().as_::<i32>().unwrap(), Some(4));
195
    }
196

197
    #[test]
198
    fn sum_constant_float() {
199
        let array = PrimitiveArray::from_iter([1., 1., 1., 1.]);
200
        let result = sum(array.as_ref()).unwrap();
201
        assert_eq!(result.as_primitive().as_::<f32>().unwrap(), Some(4.));
202
    }
203

204
    #[test]
205
    fn sum_boolean() {
206
        let array = BoolArray::from_iter([true, false, false, true]);
207
        let result = sum(array.as_ref()).unwrap();
208
        assert_eq!(result.as_primitive().as_::<i32>().unwrap(), Some(2));
209
    }
210
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc