vortex_array/compute/arrays/
is_null.rs1use 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 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 }
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}