/Users/andrewlamb/Software/arrow-rs/arrow-array/src/array/null_array.rs
Line | Count | Source |
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 | | |
20 | | use crate::builder::NullBuilder; |
21 | | use crate::{Array, ArrayRef}; |
22 | | use arrow_buffer::buffer::NullBuffer; |
23 | | use arrow_data::{ArrayData, ArrayDataBuilder}; |
24 | | use arrow_schema::DataType; |
25 | | use std::any::Any; |
26 | | use 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)] |
46 | | pub struct NullArray { |
47 | | len: usize, |
48 | | } |
49 | | |
50 | | impl 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 | 0 | pub fn new(length: usize) -> Self { |
57 | 0 | Self { len: length } |
58 | 0 | } |
59 | | |
60 | | /// Returns a zero-copy slice of this array with the indicated offset and length. |
61 | 0 | pub fn slice(&self, offset: usize, len: usize) -> Self { |
62 | 0 | assert!( |
63 | 0 | offset.saturating_add(len) <= self.len, |
64 | 0 | "the length + offset of the sliced BooleanBuffer cannot exceed the existing length" |
65 | | ); |
66 | | |
67 | 0 | Self { len } |
68 | 0 | } |
69 | | |
70 | | /// Returns a new null array builder |
71 | | /// |
72 | | /// Note that the `capacity` parameter to this function is _deprecated_. It |
73 | | /// now does nothing, and will be removed in a future version. |
74 | 0 | pub fn builder(_capacity: usize) -> NullBuilder { |
75 | 0 | NullBuilder::new() |
76 | 0 | } |
77 | | } |
78 | | |
79 | | impl Array for NullArray { |
80 | 0 | fn as_any(&self) -> &dyn Any { |
81 | 0 | self |
82 | 0 | } |
83 | | |
84 | 0 | fn to_data(&self) -> ArrayData { |
85 | 0 | self.clone().into() |
86 | 0 | } |
87 | | |
88 | 0 | fn into_data(self) -> ArrayData { |
89 | 0 | self.into() |
90 | 0 | } |
91 | | |
92 | 0 | fn data_type(&self) -> &DataType { |
93 | 0 | &DataType::Null |
94 | 0 | } |
95 | | |
96 | 0 | fn slice(&self, offset: usize, length: usize) -> ArrayRef { |
97 | 0 | Arc::new(self.slice(offset, length)) |
98 | 0 | } |
99 | | |
100 | 0 | fn len(&self) -> usize { |
101 | 0 | self.len |
102 | 0 | } |
103 | | |
104 | 0 | fn is_empty(&self) -> bool { |
105 | 0 | self.len == 0 |
106 | 0 | } |
107 | | |
108 | 0 | fn offset(&self) -> usize { |
109 | 0 | 0 |
110 | 0 | } |
111 | | |
112 | 0 | fn nulls(&self) -> Option<&NullBuffer> { |
113 | 0 | None |
114 | 0 | } |
115 | | |
116 | 0 | fn logical_nulls(&self) -> Option<NullBuffer> { |
117 | 0 | (self.len != 0).then(|| NullBuffer::new_null(self.len)) |
118 | 0 | } |
119 | | |
120 | 0 | fn is_nullable(&self) -> bool { |
121 | 0 | !self.is_empty() |
122 | 0 | } |
123 | | |
124 | 0 | fn logical_null_count(&self) -> usize { |
125 | 0 | self.len |
126 | 0 | } |
127 | | |
128 | 0 | fn get_buffer_memory_size(&self) -> usize { |
129 | 0 | 0 |
130 | 0 | } |
131 | | |
132 | 0 | fn get_array_memory_size(&self) -> usize { |
133 | 0 | std::mem::size_of::<Self>() |
134 | 0 | } |
135 | | } |
136 | | |
137 | | impl From<ArrayData> for NullArray { |
138 | 0 | fn from(data: ArrayData) -> Self { |
139 | 0 | assert_eq!( |
140 | 0 | data.data_type(), |
141 | | &DataType::Null, |
142 | 0 | "NullArray data type should be Null" |
143 | | ); |
144 | 0 | assert_eq!( |
145 | 0 | data.buffers().len(), |
146 | | 0, |
147 | 0 | "NullArray data should contain 0 buffers" |
148 | | ); |
149 | 0 | assert!( |
150 | 0 | data.nulls().is_none(), |
151 | 0 | "NullArray data should not contain a null buffer, as no buffers are required" |
152 | | ); |
153 | 0 | Self { len: data.len() } |
154 | 0 | } |
155 | | } |
156 | | |
157 | | impl From<NullArray> for ArrayData { |
158 | 0 | fn from(array: NullArray) -> Self { |
159 | 0 | let builder = ArrayDataBuilder::new(DataType::Null).len(array.len); |
160 | 0 | unsafe { builder.build_unchecked() } |
161 | 0 | } |
162 | | } |
163 | | |
164 | | impl std::fmt::Debug for NullArray { |
165 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
166 | 0 | write!(f, "NullArray({})", self.len()) |
167 | 0 | } |
168 | | } |
169 | | |
170 | | #[cfg(test)] |
171 | | mod tests { |
172 | | use super::*; |
173 | | use crate::{make_array, Int64Array, StructArray}; |
174 | | use arrow_data::transform::MutableArrayData; |
175 | | use arrow_schema::Field; |
176 | | |
177 | | #[test] |
178 | | fn test_null_array() { |
179 | | let null_arr = NullArray::new(32); |
180 | | |
181 | | assert_eq!(null_arr.len(), 32); |
182 | | assert_eq!(null_arr.null_count(), 0); |
183 | | assert_eq!(null_arr.logical_null_count(), 32); |
184 | | assert_eq!(null_arr.logical_nulls().unwrap().null_count(), 32); |
185 | | assert!(null_arr.is_valid(0)); |
186 | | assert!(null_arr.is_nullable()); |
187 | | } |
188 | | |
189 | | #[test] |
190 | | fn test_null_array_slice() { |
191 | | let array1 = NullArray::new(32); |
192 | | |
193 | | let array2 = array1.slice(8, 16); |
194 | | assert_eq!(array2.len(), 16); |
195 | | assert_eq!(array2.null_count(), 0); |
196 | | assert_eq!(array2.logical_null_count(), 16); |
197 | | assert_eq!(array2.logical_nulls().unwrap().null_count(), 16); |
198 | | assert!(array2.is_valid(0)); |
199 | | assert!(array2.is_nullable()); |
200 | | } |
201 | | |
202 | | #[test] |
203 | | fn test_debug_null_array() { |
204 | | let array = NullArray::new(1024 * 1024); |
205 | | assert_eq!(format!("{array:?}"), "NullArray(1048576)"); |
206 | | } |
207 | | |
208 | | #[test] |
209 | | fn test_null_array_with_parent_null_buffer() { |
210 | | let null_array = NullArray::new(1); |
211 | | let int_array = Int64Array::from(vec![42]); |
212 | | |
213 | | let fields = vec![ |
214 | | Field::new("a", DataType::Int64, true), |
215 | | Field::new("b", DataType::Null, true), |
216 | | ]; |
217 | | |
218 | | let struct_array_data = ArrayData::builder(DataType::Struct(fields.into())) |
219 | | .len(1) |
220 | | .add_child_data(int_array.to_data()) |
221 | | .add_child_data(null_array.to_data()) |
222 | | .build() |
223 | | .unwrap(); |
224 | | |
225 | | let mut mutable = MutableArrayData::new(vec![&struct_array_data], true, 1); |
226 | | |
227 | | // Simulate a NULL value in the parent array, for instance, if array being queried by |
228 | | // invalid index |
229 | | mutable.extend_nulls(1); |
230 | | let data = mutable.freeze(); |
231 | | |
232 | | let struct_array = Arc::new(StructArray::from(data.clone())); |
233 | | assert!(make_array(data) == struct_array); |
234 | | } |
235 | | } |