Skip to content

Commit a8fcdba

Browse files
authored
Feature: Conversion from PrimitiveVector to PrimitiveArray (#5114)
Tracking Issue: #5028 Eventually, we want to have a general `Vector` -> `Array` function that takes a `DType`, but we can wait for when we have more vectors implemented. --------- Signed-off-by: Connor Tsui <[email protected]>
1 parent d123c58 commit a8fcdba

File tree

1 file changed

+97
-2
lines changed

1 file changed

+97
-2
lines changed

vortex-array/src/arrays/primitive/array/conversion.rs

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,44 @@
44
//! Conversion methods and trait implementations of [`From`] and [`Into`] for [`PrimitiveArray`].
55
66
use vortex_buffer::{BitBufferMut, Buffer, BufferMut};
7-
use vortex_dtype::NativePType;
8-
use vortex_error::vortex_panic;
7+
use vortex_dtype::{NativePType, Nullability};
8+
use vortex_error::{VortexResult, vortex_ensure, vortex_panic};
9+
use vortex_vector::{PrimitiveVector, VectorOps, match_each_pvector};
910

1011
use crate::arrays::PrimitiveArray;
1112
use crate::validity::Validity;
1213
use crate::vtable::ValidityHelper;
1314
use crate::{ArrayRef, IntoArray};
1415

1516
impl PrimitiveArray {
17+
/// Attempts to create a `PrimitiveArray` from a [`PrimitiveVector`] given a [`Nullability`].
18+
///
19+
/// # Errors
20+
///
21+
/// Returns an error if the nullability is [`NonNullable`](Nullability::NonNullable) and there
22+
/// are nulls present in the vector.
23+
pub fn try_from_vector(
24+
primitive_vector: PrimitiveVector,
25+
nullability: Nullability,
26+
) -> VortexResult<Self> {
27+
// If we want to create a non-nullable array, then the vector should not have any nulls.
28+
vortex_ensure!(
29+
nullability.is_nullable() || primitive_vector.validity().all_true(),
30+
"tried to create a non-nullable `PrimitiveArray` from a `PrimitiveVector` that had nulls"
31+
);
32+
33+
match_each_pvector!(primitive_vector, |v| {
34+
let (buffer, mask) = v.into_parts();
35+
debug_assert_eq!(buffer.len(), mask.len());
36+
37+
let validity = Validity::from_mask(mask, nullability);
38+
39+
// SAFETY: Since the buffer and the mask came from a valid vector, we know that the
40+
// length of the buffer and the validity are the same.
41+
Ok(unsafe { Self::new_unchecked(buffer, validity) })
42+
})
43+
}
44+
1645
/// Create a PrimitiveArray from an iterator of `T`.
1746
/// NOTE: we cannot impl FromIterator trait since it conflicts with `FromIterator<T>`.
1847
pub fn from_option_iter<T: NativePType, I: IntoIterator<Item = Option<T>>>(iter: I) -> Self {
@@ -107,3 +136,69 @@ impl<T: NativePType> IntoArray for BufferMut<T> {
107136
self.freeze().into_array()
108137
}
109138
}
139+
140+
#[cfg(test)]
141+
mod tests {
142+
use vortex_buffer::BufferMut;
143+
use vortex_dtype::{Nullability, PType};
144+
use vortex_mask::MaskMut;
145+
use vortex_vector::PVector;
146+
147+
use super::*;
148+
149+
#[test]
150+
fn test_try_from_vector_with_nulls_nullable() {
151+
// Create a vector with some null values: [Some(1), None, Some(3), Some(4), None].
152+
let mut values = BufferMut::<i32>::with_capacity(5);
153+
values.extend_from_slice(&[1, 0, 3, 4, 0]);
154+
155+
let mut validity = MaskMut::with_capacity(5);
156+
validity.append_n(true, 1);
157+
validity.append_n(false, 1);
158+
validity.append_n(true, 1);
159+
validity.append_n(true, 1);
160+
validity.append_n(false, 1);
161+
162+
let pvector =
163+
PVector::try_new(values.freeze(), validity.freeze()).expect("Failed to create PVector");
164+
165+
// This should succeed since we're allowing nulls.
166+
let result =
167+
PrimitiveArray::try_from_vector(pvector.into(), Nullability::Nullable).unwrap();
168+
169+
assert_eq!(result.len(), 5);
170+
assert_eq!(result.ptype(), PType::I32);
171+
assert!(result.is_valid(0));
172+
assert!(!result.is_valid(1));
173+
assert!(result.is_valid(2));
174+
assert!(result.is_valid(3));
175+
assert!(!result.is_valid(4));
176+
}
177+
178+
#[test]
179+
fn test_try_from_vector_non_nullable_with_nulls_errors() {
180+
// Create a vector with null values: [Some(1), None, Some(3)].
181+
let mut values = BufferMut::<i32>::with_capacity(3);
182+
values.extend_from_slice(&[1, 0, 3]);
183+
184+
let mut validity = MaskMut::with_capacity(3);
185+
validity.append_n(true, 1);
186+
validity.append_n(false, 1);
187+
validity.append_n(true, 1);
188+
189+
let pvector =
190+
PVector::try_new(values.freeze(), validity.freeze()).expect("Failed to create PVector");
191+
192+
// This should fail because we're trying to create a non-nullable array from data with
193+
// nulls.
194+
let result = PrimitiveArray::try_from_vector(pvector.into(), Nullability::NonNullable);
195+
196+
assert!(result.is_err());
197+
assert!(
198+
result
199+
.unwrap_err()
200+
.to_string()
201+
.contains("non-nullable `PrimitiveArray`")
202+
);
203+
}
204+
}

0 commit comments

Comments
 (0)