• 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

0.0
/vortex-array/src/compute/mask.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 arrow_array::BooleanArray;
8
use vortex_dtype::DType;
9
use vortex_error::{VortexError, VortexResult, vortex_bail, vortex_err};
10
use vortex_mask::Mask;
11
use vortex_scalar::Scalar;
12

13
use crate::arrays::ConstantArray;
14
use crate::arrow::{FromArrowArray, IntoArrowArray};
15
use crate::compute::{ComputeFn, ComputeFnVTable, InvocationArgs, Kernel, Output, cast};
16
use crate::vtable::VTable;
17
use crate::{Array, ArrayRef, IntoArray};
18

UNCOV
19
static MASK_FN: LazyLock<ComputeFn> = LazyLock::new(|| {
×
UNCOV
20
    let compute = ComputeFn::new("mask".into(), ArcRef::new_ref(&MaskFn));
×
UNCOV
21
    for kernel in inventory::iter::<MaskKernelRef> {
×
UNCOV
22
        compute.register_kernel(kernel.0.clone());
×
UNCOV
23
    }
×
UNCOV
24
    compute
×
UNCOV
25
});
×
26

27
/// Replace values with null where the mask is true.
28
///
29
/// The returned array is nullable but otherwise has the same dtype and length as `array`.
30
///
31
/// # Examples
32
///
33
/// ```
34
/// use vortex_array::IntoArray;
35
/// use vortex_array::arrays::{BoolArray, PrimitiveArray};
36
/// use vortex_array::compute::{ mask};
37
/// use vortex_mask::Mask;
38
/// use vortex_scalar::Scalar;
39
///
40
/// let array =
41
///     PrimitiveArray::from_option_iter([Some(0i32), None, Some(1i32), None, Some(2i32)]);
42
/// let mask_array = Mask::try_from(
43
///     &BoolArray::from_iter([true, false, false, false, true]),
44
/// )
45
/// .unwrap();
46
///
47
/// let masked = mask(array.as_ref(), &mask_array).unwrap();
48
/// assert_eq!(masked.len(), 5);
49
/// assert!(!masked.is_valid(0).unwrap());
50
/// assert!(!masked.is_valid(1).unwrap());
51
/// assert_eq!(masked.scalar_at(2).unwrap(), Scalar::from(Some(1)));
52
/// assert!(!masked.is_valid(3).unwrap());
53
/// assert!(!masked.is_valid(4).unwrap());
54
/// ```
55
///
UNCOV
56
pub fn mask(array: &dyn Array, mask: &Mask) -> VortexResult<ArrayRef> {
×
UNCOV
57
    MASK_FN
×
UNCOV
58
        .invoke(&InvocationArgs {
×
UNCOV
59
            inputs: &[array.into(), mask.into()],
×
UNCOV
60
            options: &(),
×
UNCOV
61
        })?
×
UNCOV
62
        .unwrap_array()
×
UNCOV
63
}
×
64

65
pub struct MaskKernelRef(ArcRef<dyn Kernel>);
66
inventory::collect!(MaskKernelRef);
67

68
pub trait MaskKernel: VTable {
69
    /// Replace masked values with null in array.
70
    fn mask(&self, array: &Self::Array, mask: &Mask) -> VortexResult<ArrayRef>;
71
}
72

73
#[derive(Debug)]
74
pub struct MaskKernelAdapter<V: VTable>(pub V);
75

76
impl<V: VTable + MaskKernel> MaskKernelAdapter<V> {
77
    pub const fn lift(&'static self) -> MaskKernelRef {
×
78
        MaskKernelRef(ArcRef::new_ref(self))
×
79
    }
×
80
}
81

82
impl<V: VTable + MaskKernel> Kernel for MaskKernelAdapter<V> {
UNCOV
83
    fn invoke(&self, args: &InvocationArgs) -> VortexResult<Option<Output>> {
×
UNCOV
84
        let inputs = MaskArgs::try_from(args)?;
×
UNCOV
85
        let Some(array) = inputs.array.as_opt::<V>() else {
×
UNCOV
86
            return Ok(None);
×
87
        };
UNCOV
88
        Ok(Some(V::mask(&self.0, array, inputs.mask)?.into()))
×
UNCOV
89
    }
×
90
}
91

92
struct MaskFn;
93

94
impl ComputeFnVTable for MaskFn {
UNCOV
95
    fn invoke(
×
UNCOV
96
        &self,
×
UNCOV
97
        args: &InvocationArgs,
×
UNCOV
98
        kernels: &[ArcRef<dyn Kernel>],
×
UNCOV
99
    ) -> VortexResult<Output> {
×
UNCOV
100
        let MaskArgs { array, mask } = MaskArgs::try_from(args)?;
×
101

UNCOV
102
        if matches!(mask, Mask::AllFalse(_)) {
×
103
            // Fast-path for empty mask
UNCOV
104
            return Ok(cast(array, &array.dtype().as_nullable())?.into());
×
UNCOV
105
        }
×
106

UNCOV
107
        if matches!(mask, Mask::AllTrue(_)) {
×
108
            // Fast-path for full mask.
UNCOV
109
            return Ok(ConstantArray::new(
×
UNCOV
110
                Scalar::null(array.dtype().clone().as_nullable()),
×
UNCOV
111
                array.len(),
×
UNCOV
112
            )
×
UNCOV
113
            .into_array()
×
UNCOV
114
            .into());
×
UNCOV
115
        }
×
116

UNCOV
117
        for kernel in kernels {
×
UNCOV
118
            if let Some(output) = kernel.invoke(args)? {
×
UNCOV
119
                return Ok(output);
×
UNCOV
120
            }
×
121
        }
UNCOV
122
        if let Some(output) = array.invoke(&MASK_FN, args)? {
×
123
            return Ok(output);
×
UNCOV
124
        }
×
125

126
        // Fallback: implement using Arrow kernels.
UNCOV
127
        log::debug!("No mask implementation found for {}", array.encoding_id());
×
128

UNCOV
129
        let array_ref = array.to_array().into_arrow_preferred()?;
×
UNCOV
130
        let mask = BooleanArray::new(mask.to_boolean_buffer(), None);
×
131

UNCOV
132
        let masked = arrow_select::nullif::nullif(array_ref.as_ref(), &mask)?;
×
133

UNCOV
134
        Ok(ArrayRef::from_arrow(masked.as_ref(), true).into())
×
UNCOV
135
    }
×
136

UNCOV
137
    fn return_dtype(&self, args: &InvocationArgs) -> VortexResult<DType> {
×
UNCOV
138
        let MaskArgs { array, .. } = MaskArgs::try_from(args)?;
×
UNCOV
139
        Ok(array.dtype().as_nullable())
×
UNCOV
140
    }
×
141

UNCOV
142
    fn return_len(&self, args: &InvocationArgs) -> VortexResult<usize> {
×
UNCOV
143
        let MaskArgs { array, mask } = MaskArgs::try_from(args)?;
×
144

UNCOV
145
        if mask.len() != array.len() {
×
146
            vortex_bail!(
×
147
                "mask.len() is {}, does not equal array.len() of {}",
×
148
                mask.len(),
×
149
                array.len()
×
150
            );
UNCOV
151
        }
×
152

UNCOV
153
        Ok(mask.len())
×
UNCOV
154
    }
×
155

UNCOV
156
    fn is_elementwise(&self) -> bool {
×
UNCOV
157
        true
×
UNCOV
158
    }
×
159
}
160

161
struct MaskArgs<'a> {
162
    array: &'a dyn Array,
163
    mask: &'a Mask,
164
}
165

166
impl<'a> TryFrom<&InvocationArgs<'a>> for MaskArgs<'a> {
167
    type Error = VortexError;
168

UNCOV
169
    fn try_from(value: &InvocationArgs<'a>) -> Result<Self, Self::Error> {
×
UNCOV
170
        if value.inputs.len() != 2 {
×
171
            vortex_bail!("Mask function requires 2 arguments");
×
UNCOV
172
        }
×
UNCOV
173
        let array = value.inputs[0]
×
UNCOV
174
            .array()
×
UNCOV
175
            .ok_or_else(|| vortex_err!("Expected input 0 to be an array"))?;
×
UNCOV
176
        let mask = value.inputs[1]
×
UNCOV
177
            .mask()
×
UNCOV
178
            .ok_or_else(|| vortex_err!("Expected input 1 to be a mask"))?;
×
179

UNCOV
180
        Ok(MaskArgs { array, mask })
×
UNCOV
181
    }
×
182
}
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