vortex_array/compute/arrays/
is_null.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::hash::Hasher;
5use std::ops::Not;
6
7use vortex_dtype::DType;
8use vortex_dtype::Nullability::NonNullable;
9use vortex_error::VortexResult;
10use vortex_mask::Mask;
11use vortex_vector::VectorOps;
12use vortex_vector::bool::BoolVector;
13
14use crate::execution::{BatchKernelRef, BindCtx, kernel};
15use crate::stats::{ArrayStats, StatsSetRef};
16use crate::vtable::{ArrayVTable, NotSupported, OperatorVTable, VTable, VisitorVTable};
17use crate::{
18    ArrayBufferVisitor, ArrayChildVisitor, ArrayEq, ArrayHash, ArrayRef, EncodingId, EncodingRef,
19    Precision, vtable,
20};
21
22vtable!(IsNull);
23
24#[derive(Debug, Clone)]
25pub struct IsNullArray {
26    child: ArrayRef,
27    stats: ArrayStats,
28}
29
30impl IsNullArray {
31    /// Create a new is_null array.
32    pub fn new(child: ArrayRef) -> Self {
33        Self {
34            child,
35            stats: ArrayStats::default(),
36        }
37    }
38}
39
40#[derive(Debug, Clone)]
41pub struct IsNullEncoding;
42
43impl VTable for IsNullVTable {
44    type Array = IsNullArray;
45    type Encoding = IsNullEncoding;
46    type ArrayVTable = Self;
47    type CanonicalVTable = NotSupported;
48    type OperationsVTable = NotSupported;
49    type ValidityVTable = NotSupported;
50    type VisitorVTable = Self;
51    type ComputeVTable = NotSupported;
52    type EncodeVTable = NotSupported;
53    type SerdeVTable = NotSupported;
54    type OperatorVTable = Self;
55
56    fn id(_encoding: &Self::Encoding) -> EncodingId {
57        EncodingId::from("vortex.is_null")
58    }
59
60    fn encoding(_array: &Self::Array) -> EncodingRef {
61        EncodingRef::from(IsNullEncoding.as_ref())
62    }
63}
64
65impl ArrayVTable<IsNullVTable> for IsNullVTable {
66    fn len(array: &IsNullArray) -> usize {
67        array.child.len()
68    }
69
70    fn dtype(_array: &IsNullArray) -> &DType {
71        &DType::Bool(NonNullable)
72    }
73
74    fn stats(array: &IsNullArray) -> StatsSetRef<'_> {
75        array.stats.to_ref(array.as_ref())
76    }
77
78    fn array_hash<H: Hasher>(array: &IsNullArray, state: &mut H, precision: Precision) {
79        array.child.array_hash(state, precision);
80    }
81
82    fn array_eq(array: &IsNullArray, other: &IsNullArray, precision: Precision) -> bool {
83        array.child.array_eq(&other.child, precision)
84    }
85}
86
87impl VisitorVTable<IsNullVTable> for IsNullVTable {
88    fn visit_buffers(_array: &IsNullArray, _visitor: &mut dyn ArrayBufferVisitor) {
89        // No buffers
90    }
91
92    fn visit_children(array: &IsNullArray, visitor: &mut dyn ArrayChildVisitor) {
93        visitor.visit_child("child", array.child.as_ref());
94    }
95}
96
97impl OperatorVTable<IsNullVTable> for IsNullVTable {
98    fn bind(
99        array: &IsNullArray,
100        selection: Option<&ArrayRef>,
101        ctx: &mut dyn BindCtx,
102    ) -> VortexResult<BatchKernelRef> {
103        let child = ctx.bind(&array.child, selection)?;
104        Ok(kernel(move || {
105            let child = child.execute()?;
106            let is_null = child.validity().not().to_bit_buffer();
107            Ok(BoolVector::new(is_null, Mask::AllTrue(child.len())).into())
108        }))
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use std::ops::Not;
115
116    use vortex_buffer::{bitbuffer, buffer};
117    use vortex_error::VortexResult;
118    use vortex_vector::VectorOps;
119
120    use crate::IntoArray;
121    use crate::arrays::PrimitiveArray;
122    use crate::compute::arrays::is_null::IsNullArray;
123    use crate::validity::Validity;
124
125    #[test]
126    fn test_is_null() -> VortexResult<()> {
127        let validity = bitbuffer![1 0 1];
128        let array = PrimitiveArray::new(
129            buffer![0, 1, 2],
130            Validity::Array(validity.clone().into_array()),
131        )
132        .into_array();
133
134        let result = IsNullArray::new(array).execute()?.into_bool();
135        assert!(result.validity().all_true());
136        assert_eq!(result.bits(), &validity.not());
137
138        Ok(())
139    }
140}