Skip to main content

arrow_array/array/
null_array.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Contains the `NullArray` type.
19
20use crate::builder::NullBuilder;
21use crate::{Array, ArrayRef};
22use arrow_buffer::buffer::NullBuffer;
23use arrow_data::{ArrayData, ArrayDataBuilder};
24use arrow_schema::DataType;
25use std::any::Any;
26use std::sync::Arc;
27
28/// An array of [null values](https://arrow.apache.org/docs/format/Columnar.html#null-layout)
29///
30/// A `NullArray` is a simplified array where all values are null.
31///
32/// # Example: Create an array
33///
34/// ```
35/// use arrow_array::{Array, NullArray};
36///
37/// let array = NullArray::new(10);
38///
39/// assert!(array.is_nullable());
40/// assert_eq!(array.len(), 10);
41/// assert_eq!(array.null_count(), 0);
42/// assert_eq!(array.logical_null_count(), 10);
43/// assert_eq!(array.logical_nulls().unwrap().null_count(), 10);
44/// ```
45#[derive(Clone)]
46pub struct NullArray {
47    len: usize,
48}
49
50impl NullArray {
51    /// Create a new [`NullArray`] of the specified length
52    ///
53    /// *Note*: Use [`crate::array::new_null_array`] if you need an array of some
54    /// other [`DataType`].
55    ///
56    pub fn new(length: usize) -> Self {
57        Self { len: length }
58    }
59
60    /// Returns a zero-copy slice of this array with the indicated offset and length.
61    ///
62    /// # Panics
63    /// Panics if `offset + len > self.len()`
64    pub fn slice(&self, offset: usize, len: usize) -> Self {
65        assert!(
66            offset.saturating_add(len) <= self.len,
67            "the length + offset of the sliced BooleanBuffer cannot exceed the existing length"
68        );
69
70        Self { len }
71    }
72
73    /// Returns a new null array builder
74    ///
75    /// Note that the `capacity` parameter to this function is _deprecated_. It
76    /// now does nothing, and will be removed in a future version.
77    pub fn builder(_capacity: usize) -> NullBuilder {
78        NullBuilder::new()
79    }
80}
81
82/// SAFETY: Correctly implements the contract of Arrow Arrays
83unsafe impl Array for NullArray {
84    fn as_any(&self) -> &dyn Any {
85        self
86    }
87
88    fn to_data(&self) -> ArrayData {
89        self.clone().into()
90    }
91
92    fn into_data(self) -> ArrayData {
93        self.into()
94    }
95
96    fn data_type(&self) -> &DataType {
97        &DataType::Null
98    }
99
100    fn slice(&self, offset: usize, length: usize) -> ArrayRef {
101        Arc::new(self.slice(offset, length))
102    }
103
104    fn len(&self) -> usize {
105        self.len
106    }
107
108    fn is_empty(&self) -> bool {
109        self.len == 0
110    }
111
112    fn offset(&self) -> usize {
113        0
114    }
115
116    fn nulls(&self) -> Option<&NullBuffer> {
117        None
118    }
119
120    fn logical_nulls(&self) -> Option<NullBuffer> {
121        (self.len != 0).then(|| NullBuffer::new_null(self.len))
122    }
123
124    fn is_nullable(&self) -> bool {
125        !self.is_empty()
126    }
127
128    fn logical_null_count(&self) -> usize {
129        self.len
130    }
131
132    fn get_buffer_memory_size(&self) -> usize {
133        0
134    }
135
136    fn get_array_memory_size(&self) -> usize {
137        std::mem::size_of::<Self>()
138    }
139
140    #[cfg(feature = "pool")]
141    fn claim(&self, _pool: &dyn arrow_buffer::MemoryPool) {
142        // NullArray has no buffers to claim
143    }
144}
145
146impl From<ArrayData> for NullArray {
147    fn from(data: ArrayData) -> Self {
148        let (data_type, len, nulls, _offset, buffers, _child_data) = data.into_parts();
149
150        assert_eq!(
151            data_type,
152            DataType::Null,
153            "NullArray data type should be Null"
154        );
155        assert_eq!(buffers.len(), 0, "NullArray data should contain 0 buffers");
156        assert!(
157            nulls.is_none(),
158            "NullArray data should not contain a null buffer, as no buffers are required"
159        );
160        Self { len }
161    }
162}
163
164impl From<NullArray> for ArrayData {
165    fn from(array: NullArray) -> Self {
166        let builder = ArrayDataBuilder::new(DataType::Null).len(array.len);
167        unsafe { builder.build_unchecked() }
168    }
169}
170
171impl std::fmt::Debug for NullArray {
172    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
173        write!(f, "NullArray({})", self.len())
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use crate::{Int64Array, StructArray, make_array};
181    use arrow_data::transform::MutableArrayData;
182    use arrow_schema::Field;
183
184    #[test]
185    fn test_null_array() {
186        let null_arr = NullArray::new(32);
187
188        assert_eq!(null_arr.len(), 32);
189        assert_eq!(null_arr.null_count(), 0);
190        assert_eq!(null_arr.logical_null_count(), 32);
191        assert_eq!(null_arr.logical_nulls().unwrap().null_count(), 32);
192        assert!(null_arr.is_valid(0));
193        assert!(null_arr.is_nullable());
194    }
195
196    #[test]
197    fn test_null_array_slice() {
198        let array1 = NullArray::new(32);
199
200        let array2 = array1.slice(8, 16);
201        assert_eq!(array2.len(), 16);
202        assert_eq!(array2.null_count(), 0);
203        assert_eq!(array2.logical_null_count(), 16);
204        assert_eq!(array2.logical_nulls().unwrap().null_count(), 16);
205        assert!(array2.is_valid(0));
206        assert!(array2.is_nullable());
207    }
208
209    #[test]
210    fn test_debug_null_array() {
211        let array = NullArray::new(1024 * 1024);
212        assert_eq!(format!("{array:?}"), "NullArray(1048576)");
213    }
214
215    #[test]
216    fn test_null_array_with_parent_null_buffer() {
217        let null_array = NullArray::new(1);
218        let int_array = Int64Array::from(vec![42]);
219
220        let fields = vec![
221            Field::new("a", DataType::Int64, true),
222            Field::new("b", DataType::Null, true),
223        ];
224
225        let struct_array_data = ArrayData::builder(DataType::Struct(fields.into()))
226            .len(1)
227            .add_child_data(int_array.to_data())
228            .add_child_data(null_array.to_data())
229            .build()
230            .unwrap();
231
232        let mut mutable = MutableArrayData::new(vec![&struct_array_data], true, 1);
233
234        // Simulate a NULL value in the parent array, for instance, if array being queried by
235        // invalid index
236        mutable.try_extend_nulls(1).unwrap();
237        let data = mutable.freeze();
238
239        let struct_array = Arc::new(StructArray::from(data.clone()));
240        assert!(make_array(data) == struct_array);
241    }
242}