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

vortex-data / vortex / 16204612549

10 Jul 2025 07:50PM UTC coverage: 81.152% (+2.9%) from 78.263%
16204612549

Pull #3825

github

web-flow
Merge d0d2717da into be9c2fd3e
Pull Request #3825: feat: Add optimize ArrayOp with VBView implementation

178 of 211 new or added lines in 4 files covered. (84.36%)

330 existing lines in 34 files now uncovered.

45433 of 55985 relevant lines covered (81.15%)

145951.87 hits per line

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

80.16
/vortex-expr/src/exprs/is_null.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use std::fmt::Display;
5
use std::ops::Not;
6

7
use vortex_array::arrays::{BoolArray, ConstantArray};
8
use vortex_array::{Array, ArrayRef, DeserializeMetadata, EmptyMetadata, IntoArray};
9
use vortex_dtype::{DType, Nullability};
10
use vortex_error::{VortexResult, vortex_bail};
11
use vortex_mask::Mask;
12

13
use crate::{AnalysisExpr, ExprEncodingRef, ExprId, ExprRef, IntoExpr, Scope, VTable, vtable};
14

15
vtable!(IsNull);
16

17
#[allow(clippy::derived_hash_with_manual_eq)]
18
#[derive(Clone, Debug, Hash)]
19
pub struct IsNullExpr {
20
    child: ExprRef,
21
}
22

23
impl PartialEq for IsNullExpr {
UNCOV
24
    fn eq(&self, other: &Self) -> bool {
×
UNCOV
25
        self.child.eq(&other.child)
×
26
    }
×
27
}
28

29
pub struct IsNullExprEncoding;
30

31
impl VTable for IsNullVTable {
32
    type Expr = IsNullExpr;
33
    type Encoding = IsNullExprEncoding;
34
    type Metadata = EmptyMetadata;
35

UNCOV
36
    fn id(_encoding: &Self::Encoding) -> ExprId {
×
UNCOV
37
        ExprId::new_ref("is_null")
×
38
    }
×
39

40
    fn encoding(_expr: &Self::Expr) -> ExprEncodingRef {
×
UNCOV
41
        ExprEncodingRef::new_ref(IsNullExprEncoding.as_ref())
×
42
    }
×
43

44
    fn metadata(_expr: &Self::Expr) -> Option<Self::Metadata> {
×
UNCOV
45
        Some(EmptyMetadata)
×
46
    }
×
47

48
    fn children(expr: &Self::Expr) -> Vec<&ExprRef> {
1✔
49
        vec![&expr.child]
1✔
50
    }
1✔
51

52
    fn with_children(_expr: &Self::Expr, children: Vec<ExprRef>) -> VortexResult<Self::Expr> {
1✔
53
        Ok(IsNullExpr::new(children[0].clone()))
1✔
54
    }
1✔
55

UNCOV
56
    fn build(
×
UNCOV
57
        _encoding: &Self::Encoding,
×
58
        _metadata: &<Self::Metadata as DeserializeMetadata>::Output,
×
59
        children: Vec<ExprRef>,
×
60
    ) -> VortexResult<Self::Expr> {
×
61
        if children.len() != 1 {
×
62
            vortex_bail!("IsNull expects exactly one child, got {}", children.len());
×
63
        }
×
64
        Ok(IsNullExpr::new(children[0].clone()))
×
65
    }
×
66

67
    fn evaluate(expr: &Self::Expr, scope: &Scope) -> VortexResult<ArrayRef> {
24✔
68
        let array = expr.child.unchecked_evaluate(scope)?;
24✔
69
        match array.validity_mask()? {
24✔
70
            Mask::AllTrue(len) => Ok(ConstantArray::new(false, len).into_array()),
1✔
71
            Mask::AllFalse(len) => Ok(ConstantArray::new(true, len).into_array()),
1✔
72
            Mask::Values(mask) => Ok(BoolArray::from(mask.boolean_buffer().not()).into_array()),
22✔
73
        }
74
    }
24✔
75

76
    fn return_dtype(_expr: &Self::Expr, _scope: &DType) -> VortexResult<DType> {
25✔
77
        Ok(DType::Bool(Nullability::NonNullable))
25✔
78
    }
25✔
79
}
80

81
impl IsNullExpr {
82
    pub fn new(child: ExprRef) -> Self {
27✔
83
        Self { child }
27✔
84
    }
27✔
85
}
86

87
impl Display for IsNullExpr {
UNCOV
88
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
UNCOV
89
        write!(f, "is_null({})", self.child)
×
90
    }
×
91
}
92

93
impl AnalysisExpr for IsNullExpr {}
94

95
pub fn is_null(child: ExprRef) -> ExprRef {
26✔
96
    IsNullExpr::new(child).into_expr()
26✔
97
}
26✔
98

99
#[cfg(test)]
100
mod tests {
101
    use vortex_array::IntoArray;
102
    use vortex_array::arrays::{PrimitiveArray, StructArray};
103
    use vortex_dtype::{DType, Nullability};
104
    use vortex_scalar::Scalar;
105

106
    use crate::is_null::is_null;
107
    use crate::{Scope, get_item, root, test_harness};
108

109
    #[test]
110
    fn dtype() {
1✔
111
        let dtype = test_harness::struct_dtype();
1✔
112
        assert_eq!(
1✔
113
            is_null(root()).return_dtype(&dtype).unwrap(),
1✔
114
            DType::Bool(Nullability::NonNullable)
1✔
115
        );
1✔
116
    }
1✔
117

118
    #[test]
119
    fn replace_children() {
1✔
120
        let expr = is_null(root());
1✔
121
        let _ = expr.with_children(vec![root()]);
1✔
122
    }
1✔
123

124
    #[test]
125
    fn evaluate_mask() {
1✔
126
        let test_array =
1✔
127
            PrimitiveArray::from_option_iter(vec![Some(1), None, Some(2), None, Some(3)])
1✔
128
                .into_array();
1✔
129
        let expected = [false, true, false, true, false];
1✔
130

1✔
131
        let result = is_null(root())
1✔
132
            .evaluate(&Scope::new(test_array.clone()))
1✔
133
            .unwrap();
1✔
134

1✔
135
        assert_eq!(result.len(), test_array.len());
1✔
136
        assert_eq!(result.dtype(), &DType::Bool(Nullability::NonNullable));
1✔
137

138
        for (i, expected_value) in expected.iter().enumerate() {
5✔
139
            assert_eq!(
5✔
140
                result.scalar_at(i).unwrap(),
5✔
141
                Scalar::bool(*expected_value, Nullability::NonNullable)
5✔
142
            );
5✔
143
        }
144
    }
1✔
145

146
    #[test]
147
    fn evaluate_all_false() {
1✔
148
        let test_array = PrimitiveArray::from_iter(vec![1, 2, 3, 4, 5]).into_array();
1✔
149

1✔
150
        let result = is_null(root())
1✔
151
            .evaluate(&Scope::new(test_array.clone()))
1✔
152
            .unwrap();
1✔
153

1✔
154
        assert_eq!(result.len(), test_array.len());
1✔
155
        assert_eq!(
1✔
156
            result.as_constant().unwrap(),
1✔
157
            Scalar::bool(false, Nullability::NonNullable)
1✔
158
        );
1✔
159
    }
1✔
160

161
    #[test]
162
    fn evaluate_all_true() {
1✔
163
        let test_array =
1✔
164
            PrimitiveArray::from_option_iter(vec![None::<i32>, None, None, None, None])
1✔
165
                .into_array();
1✔
166

1✔
167
        let result = is_null(root())
1✔
168
            .evaluate(&Scope::new(test_array.clone()))
1✔
169
            .unwrap();
1✔
170

1✔
171
        assert_eq!(result.len(), test_array.len());
1✔
172
        assert_eq!(
1✔
173
            result.as_constant().unwrap(),
1✔
174
            Scalar::bool(true, Nullability::NonNullable)
1✔
175
        );
1✔
176
    }
1✔
177

178
    #[test]
179
    fn evaluate_struct() {
1✔
180
        let test_array = StructArray::from_fields(&[(
1✔
181
            "a",
1✔
182
            PrimitiveArray::from_option_iter(vec![Some(1), None, Some(2), None, Some(3)])
1✔
183
                .into_array(),
1✔
184
        )])
1✔
185
        .unwrap()
1✔
186
        .into_array();
1✔
187
        let expected = [false, true, false, true, false];
1✔
188

1✔
189
        let result = is_null(get_item("a", root()))
1✔
190
            .evaluate(&Scope::new(test_array.clone()))
1✔
191
            .unwrap();
1✔
192

1✔
193
        assert_eq!(result.len(), test_array.len());
1✔
194
        assert_eq!(result.dtype(), &DType::Bool(Nullability::NonNullable));
1✔
195

196
        for (i, expected_value) in expected.iter().enumerate() {
5✔
197
            assert_eq!(
5✔
198
                result.scalar_at(i).unwrap(),
5✔
199
                Scalar::bool(*expected_value, Nullability::NonNullable)
5✔
200
            );
5✔
201
        }
202
    }
1✔
203
}
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