Skip to main content

vortex_array/arrays/null/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use vortex_error::VortexResult;
5use vortex_error::vortex_bail;
6use vortex_error::vortex_ensure;
7use vortex_error::vortex_ensure_eq;
8use vortex_error::vortex_panic;
9use vortex_session::VortexSession;
10use vortex_session::registry::CachedId;
11
12use crate::ArrayRef;
13use crate::ArraySlots;
14use crate::ExecutionCtx;
15use crate::ExecutionResult;
16use crate::array::Array;
17use crate::array::ArrayId;
18use crate::array::ArrayParts;
19use crate::array::ArrayView;
20use crate::array::EmptyArrayData;
21use crate::array::OperationsVTable;
22use crate::array::VTable;
23use crate::array::ValidityVTable;
24use crate::array::with_empty_buffers;
25use crate::arrays::null::compute::rules::PARENT_RULES;
26use crate::buffer::BufferHandle;
27use crate::builders::ArrayBuilder;
28use crate::builders::NullBuilder;
29use crate::dtype::DType;
30use crate::scalar::Scalar;
31use crate::serde::ArrayChildren;
32use crate::validity::Validity;
33
34pub(crate) mod compute;
35
36/// A [`Null`]-encoded Vortex array.
37pub type NullArray = Array<Null>;
38
39impl VTable for Null {
40    type TypedArrayData = EmptyArrayData;
41
42    type OperationsVTable = Self;
43    type ValidityVTable = Self;
44
45    fn id(&self) -> ArrayId {
46        static ID: CachedId = CachedId::new("vortex.null");
47        *ID
48    }
49
50    fn validate(
51        &self,
52        _data: &EmptyArrayData,
53        dtype: &DType,
54        _len: usize,
55        _slots: &[Option<ArrayRef>],
56    ) -> VortexResult<()> {
57        vortex_ensure_eq!(*dtype, DType::Null, "NullArray dtype must be DType::Null");
58        Ok(())
59    }
60
61    fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
62        0
63    }
64
65    fn buffer(_array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
66        vortex_panic!("NullArray buffer index {idx} out of bounds")
67    }
68
69    fn buffer_name(_array: ArrayView<'_, Self>, _idx: usize) -> Option<String> {
70        None
71    }
72
73    fn with_buffers(
74        &self,
75        array: ArrayView<'_, Self>,
76        buffers: &[BufferHandle],
77    ) -> VortexResult<ArrayParts<Self>> {
78        with_empty_buffers(self, array, buffers)
79    }
80
81    fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
82        vortex_panic!("NullArray slot_name index {idx} out of bounds")
83    }
84
85    fn serialize(
86        _array: ArrayView<'_, Self>,
87        _session: &VortexSession,
88    ) -> VortexResult<Option<Vec<u8>>> {
89        Ok(Some(vec![]))
90    }
91
92    fn deserialize(
93        &self,
94        dtype: &DType,
95        len: usize,
96        metadata: &[u8],
97
98        _buffers: &[BufferHandle],
99        _children: &dyn ArrayChildren,
100        _session: &VortexSession,
101    ) -> VortexResult<ArrayParts<Self>> {
102        vortex_ensure!(
103            metadata.is_empty(),
104            "NullArray expects empty metadata, got {} bytes",
105            metadata.len()
106        );
107        Ok(ArrayParts::new(
108            self.clone(),
109            dtype.clone(),
110            len,
111            EmptyArrayData,
112            ArraySlots::new(),
113        ))
114    }
115
116    fn reduce_parent(
117        array: ArrayView<'_, Self>,
118        parent: &ArrayRef,
119        child_idx: usize,
120    ) -> VortexResult<Option<ArrayRef>> {
121        PARENT_RULES.evaluate(array, parent, child_idx)
122    }
123
124    fn execute(array: Array<Self>, _ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
125        Ok(ExecutionResult::done(array))
126    }
127
128    fn append_to_builder(
129        array: ArrayView<'_, Self>,
130        builder: &mut dyn ArrayBuilder,
131        _ctx: &mut ExecutionCtx,
132    ) -> VortexResult<()> {
133        let Some(builder) = builder.as_any_mut().downcast_mut::<NullBuilder>() else {
134            vortex_bail!("append_to_builder for Null requires a NullBuilder");
135        };
136        builder.append_nulls(array.len());
137        Ok(())
138    }
139}
140
141/// A array where all values are null.
142///
143/// This mirrors the Apache Arrow Null array encoding and provides an efficient representation
144/// for arrays containing only null values. No actual data is stored, only the length.
145///
146/// All operations on null arrays return null values or indicate invalid data.
147///
148/// # Examples
149///
150/// ```
151/// # fn main() -> vortex_error::VortexResult<()> {
152/// use vortex_array::arrays::NullArray;
153/// use vortex_array::{IntoArray, VortexSessionExecute, array_session};
154///
155/// // Create a null array with 5 elements
156/// let array = NullArray::new(5);
157///
158/// // Slice the array - still contains nulls
159/// let sliced = array.slice(1..3)?;
160/// assert_eq!(sliced.len(), 2);
161///
162/// // All elements are null
163/// let mut ctx = array_session().create_execution_ctx();
164/// let scalar = array.execute_scalar(0, &mut ctx).unwrap();
165/// assert!(scalar.is_null());
166/// # Ok(())
167/// # }
168/// ```
169#[derive(Clone, Debug)]
170pub struct Null;
171
172impl Array<Null> {
173    pub fn new(len: usize) -> Self {
174        unsafe {
175            Array::from_parts_unchecked(ArrayParts::new(
176                Null,
177                DType::Null,
178                len,
179                EmptyArrayData,
180                ArraySlots::new(),
181            ))
182        }
183    }
184}
185
186impl OperationsVTable<Null> for Null {
187    type ProbeState = ();
188
189    fn scalar_at(
190        _array: ArrayView<'_, Null>,
191        _index: usize,
192        _ctx: &mut ExecutionCtx,
193    ) -> VortexResult<Scalar> {
194        Ok(Scalar::null(DType::Null))
195    }
196}
197
198impl ValidityVTable<Null> for Null {
199    fn validity(_array: ArrayView<'_, Null>) -> VortexResult<Validity> {
200        Ok(Validity::AllInvalid)
201    }
202}