Skip to content

Commit 0b266ac

Browse files
committed
add StructVectorMut ops implementation
Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
1 parent 3983af0 commit 0b266ac

File tree

3 files changed

+777
-242
lines changed

3 files changed

+777
-242
lines changed

vortex-vector/src/ops.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ pub trait VectorMutOps: private::Sealed + Into<VectorMut> {
7070
fn reserve(&mut self, additional: usize);
7171

7272
/// Extends the vector by appending elements from another vector.
73+
///
74+
/// # Panics
75+
///
76+
/// Panics if the `other` vector has the wrong type (for example, a
77+
/// [`StructVector`](crate::StructVector) might have incorrect fields).
7378
fn extend_from_vector(&mut self, other: &Self::Immutable);
7479

7580
/// Appends `n` null elements to the vector.

vortex-vector/src/struct_/vector.rs

Lines changed: 99 additions & 223 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
//! Definition and implementation of [`StructVector`].
55
6+
use vortex_error::{VortexExpect, VortexResult, vortex_ensure};
67
use vortex_mask::Mask;
78

89
use crate::{StructVectorMut, Vector, VectorMutOps, VectorOps};
@@ -18,24 +19,113 @@ pub struct StructVector {
1819
/// The fields of the `StructVector`, each stored column-wise as a [`Vector`].
1920
pub(super) fields: Box<[Vector]>,
2021

22+
/// The validity mask (where `true` represents an element is **not** null).
23+
pub(super) validity: Mask,
24+
2125
/// The length of the vector (which is the same as all field vectors).
2226
///
2327
/// This is stored here as a convenience, and also helps in the case that the `StructVector` has
2428
/// no fields.
2529
pub(super) len: usize,
30+
}
2631

27-
/// The capacity of the vector (which is the less than or equal to the capacity of all field
28-
/// vectors).
32+
impl StructVector {
33+
/// Creates a new [`StructVector`] from the given fields and validity mask.
34+
///
35+
/// # Panics
36+
///
37+
/// Panics if:
2938
///
30-
/// This is stored here as a convenience for converting to/from a [`StructVectorMut`], and also
31-
/// helps in the case that the `StructVector` has no fields.
32-
pub(super) capacity: usize,
39+
/// - Any field vector has a length that does not match the length of other fields.
40+
/// - The validity mask length does not match the field length.
41+
pub fn new(fields: Box<[Vector]>, validity: Mask) -> Self {
42+
Self::try_new(fields, validity).vortex_expect(
43+
"`StructVector` fields must have matching length and validity constraints",
44+
)
45+
}
3346

34-
/// The validity mask (where `true` represents an element is **not** null).
35-
pub(super) validity: Mask,
36-
}
47+
/// Tries to create a new [`StructVector`] from the given fields and validity mask.
48+
///
49+
/// # Errors
50+
///
51+
/// Returns an error if:
52+
///
53+
/// - Any field vector has a length that does not match the length of other fields.
54+
/// - The validity mask length does not match the field length.
55+
pub fn try_new(fields: Box<[Vector]>, validity: Mask) -> VortexResult<Self> {
56+
let len = if fields.is_empty() {
57+
validity.len()
58+
} else {
59+
fields[0].len()
60+
};
61+
62+
Self::validate(&fields, len, &validity)?;
63+
64+
Ok(Self {
65+
fields,
66+
validity,
67+
len,
68+
})
69+
}
70+
71+
/// Creates a new [`StructVector`] from the given fields and validity mask without validation.
72+
///
73+
/// # Safety
74+
///
75+
/// The caller must ensure that:
76+
///
77+
/// - All field vectors have the same length.
78+
/// - The validity mask has a length equal to the field length.
79+
pub unsafe fn new_unchecked(fields: Box<[Vector]>, validity: Mask) -> Self {
80+
let len = if fields.is_empty() {
81+
validity.len()
82+
} else {
83+
fields[0].len()
84+
};
85+
86+
debug_assert!(
87+
Self::validate(&fields, len, &validity).is_ok(),
88+
"`StructVector` fields must have matching length and validity constraints"
89+
);
90+
91+
Self {
92+
fields,
93+
validity,
94+
len,
95+
}
96+
}
97+
98+
/// Validates the fields and validity mask for a [`StructVector`].
99+
///
100+
/// # Errors
101+
///
102+
/// Returns an error if:
103+
///
104+
/// - Any field vector has a length that does not match the length of other fields.
105+
/// - The validity mask length does not match the field length.
106+
fn validate(fields: &[Vector], len: usize, validity: &Mask) -> VortexResult<()> {
107+
// Validate that the validity mask has the correct length.
108+
vortex_ensure!(
109+
validity.len() == len,
110+
"Validity mask length ({}) does not match expected length ({})",
111+
validity.len(),
112+
len
113+
);
114+
115+
// Validate that all fields have the correct length.
116+
for (i, field) in fields.iter().enumerate() {
117+
vortex_ensure!(
118+
field.len() == len,
119+
"Field {} has length {} but expected length {}",
120+
i,
121+
field.len(),
122+
len
123+
);
124+
}
125+
126+
Ok(())
127+
}
37128

38-
impl StructVector {
39129
/// Returns the fields of the `StructVector`, each stored column-wise as a [`Vector`].
40130
pub fn fields(&self) -> &[Vector] {
41131
self.fields.as_ref()
@@ -89,7 +179,6 @@ impl VectorOps for StructVector {
89179
return Err(StructVector {
90180
fields: all_fields.into_boxed_slice(),
91181
len: self.len,
92-
capacity: self.capacity,
93182
validity: validity.freeze(),
94183
});
95184
}
@@ -99,220 +188,7 @@ impl VectorOps for StructVector {
99188
Ok(StructVectorMut {
100189
fields: mutable_fields,
101190
len: self.len,
102-
capacity: self.capacity,
103191
validity,
104192
})
105193
}
106194
}
107-
108-
#[cfg(test)]
109-
mod tests {
110-
use vortex_mask::Mask;
111-
112-
use super::*;
113-
use crate::{BoolVectorMut, NullVector, PVectorMut, VectorMut};
114-
115-
// TODO(connor): Make sure to test actual logic instead of just lengths and capacity.
116-
117-
/// Helper function to create a `StructVector` with the given fields.
118-
fn create_struct_vector(fields: Vec<Vector>, len: usize, capacity: usize) -> StructVector {
119-
StructVector {
120-
fields: fields.into_boxed_slice(),
121-
len,
122-
capacity,
123-
validity: Mask::AllTrue(len),
124-
}
125-
}
126-
127-
#[test]
128-
fn test_try_into_mut_success() {
129-
// Create a `StructVector` with 3 different field types (null, bool, primitive).
130-
let null_field: Vector = NullVector::new(5).into();
131-
let bool_field: Vector = BoolVectorMut::from_iter([true, false, true, false, true])
132-
.freeze()
133-
.into();
134-
let prim_field: Vector = PVectorMut::<i32>::from_iter([10, 20, 30, 40, 50])
135-
.freeze()
136-
.into();
137-
138-
let struct_vec = create_struct_vector(vec![null_field, bool_field, prim_field], 5, 5);
139-
140-
// Attempt to convert to mutable.
141-
let result = struct_vec.try_into_mut();
142-
143-
// Should succeed since all fields have unique ownership.
144-
assert!(result.is_ok());
145-
146-
let mut_struct = result.unwrap();
147-
148-
// Verify the mutable struct has correct length and capacity.
149-
assert_eq!(mut_struct.len(), 5);
150-
assert_eq!(mut_struct.capacity(), 5);
151-
152-
// Verify that we have 3 fields.
153-
assert_eq!(mut_struct.fields.len(), 3);
154-
155-
// Verify each field has the correct type and length.
156-
assert!(matches!(mut_struct.fields[0], VectorMut::Null(_)));
157-
assert_eq!(mut_struct.fields[0].len(), 5);
158-
159-
assert!(matches!(mut_struct.fields[1], VectorMut::Bool(_)));
160-
assert_eq!(mut_struct.fields[1].len(), 5);
161-
assert!(mut_struct.fields[1].capacity() >= 5);
162-
163-
assert!(matches!(mut_struct.fields[2], VectorMut::Primitive(_)));
164-
assert_eq!(mut_struct.fields[2].len(), 5);
165-
assert!(mut_struct.fields[2].capacity() >= 5);
166-
}
167-
168-
#[test]
169-
fn test_try_into_mut_fails_first_field() {
170-
// Create a bool field with shared ownership (cloned to have Arc count > 1).
171-
let bool_field: Vector = BoolVectorMut::from_iter([true, false, true])
172-
.freeze()
173-
.into();
174-
let bool_field_clone = bool_field.clone();
175-
176-
let null_field: Vector = NullVector::new(3).into();
177-
let prim_field: Vector = PVectorMut::<i32>::from_iter([1, 2, 3]).freeze().into();
178-
179-
// Create a struct with the cloned bool field as the first field.
180-
let struct_vec = create_struct_vector(vec![bool_field_clone, null_field, prim_field], 3, 3);
181-
182-
// Attempt to convert to mutable.
183-
let result = struct_vec.try_into_mut();
184-
185-
// Should fail since the first field has shared ownership.
186-
assert!(result.is_err());
187-
188-
// Keep the original alive to ensure shared ownership is maintained.
189-
drop(bool_field);
190-
191-
let recovered_struct = result.unwrap_err();
192-
193-
// Verify the recovered struct has all 3 fields.
194-
assert_eq!(recovered_struct.fields.len(), 3);
195-
assert_eq!(recovered_struct.len(), 3);
196-
assert_eq!(recovered_struct.capacity, 3);
197-
198-
// Verify field types are preserved.
199-
assert!(matches!(recovered_struct.fields[0], Vector::Bool(_)));
200-
assert!(matches!(recovered_struct.fields[1], Vector::Null(_)));
201-
assert!(matches!(recovered_struct.fields[2], Vector::Primitive(_)));
202-
}
203-
204-
#[test]
205-
fn test_try_into_mut_fails_middle_field() {
206-
// Create a primitive field with shared ownership (cloned).
207-
let prim_field: Vector = PVectorMut::<i32>::from_iter([100, 200, 300, 400])
208-
.freeze()
209-
.into();
210-
let prim_field_clone = prim_field.clone();
211-
212-
let bool_field: Vector = BoolVectorMut::from_iter([true, false, true, false])
213-
.freeze()
214-
.into();
215-
let null_field: Vector = NullVector::new(4).into();
216-
217-
// Create a struct with the cloned primitive field as the middle field.
218-
let struct_vec = create_struct_vector(vec![bool_field, prim_field_clone, null_field], 4, 4);
219-
220-
// Attempt to convert to mutable.
221-
let result = struct_vec.try_into_mut();
222-
223-
// Should fail since the middle field has shared ownership.
224-
assert!(result.is_err());
225-
226-
// Keep the original alive to ensure shared ownership is maintained.
227-
drop(prim_field);
228-
229-
let recovered_struct = result.unwrap_err();
230-
231-
// Verify all 3 fields are present in the recovered struct.
232-
assert_eq!(recovered_struct.fields.len(), 3);
233-
assert_eq!(recovered_struct.len(), 4);
234-
assert_eq!(recovered_struct.capacity, 4);
235-
236-
// Verify field types and order are preserved.
237-
// The first field was converted to mutable then frozen back, so it should still be bool.
238-
assert!(matches!(recovered_struct.fields[0], Vector::Bool(_)));
239-
assert!(matches!(recovered_struct.fields[1], Vector::Primitive(_)));
240-
assert!(matches!(recovered_struct.fields[2], Vector::Null(_)));
241-
}
242-
243-
#[test]
244-
fn test_try_into_mut_fails_last_field() {
245-
// Create a null field with "shared ownership" (though NullVector always succeeds, we test
246-
// the pattern by cloning a bool vector).
247-
let bool_field_last: Vector = BoolVectorMut::from_iter([true, true]).freeze().into();
248-
let bool_field_last_clone = bool_field_last.clone();
249-
250-
let null_field: Vector = NullVector::new(2).into();
251-
let prim_field: Vector = PVectorMut::<u64>::from_iter([1000, 2000]).freeze().into();
252-
253-
// Create a struct with the cloned bool field as the last field.
254-
let struct_vec =
255-
create_struct_vector(vec![null_field, prim_field, bool_field_last_clone], 2, 2);
256-
257-
// Attempt to convert to mutable.
258-
let result = struct_vec.try_into_mut();
259-
260-
// Should fail since the last field has shared ownership.
261-
assert!(result.is_err());
262-
263-
// Keep the original alive to ensure shared ownership is maintained.
264-
drop(bool_field_last);
265-
266-
let recovered_struct = result.unwrap_err();
267-
268-
// Verify all 3 fields are present.
269-
assert_eq!(recovered_struct.fields.len(), 3);
270-
assert_eq!(recovered_struct.len(), 2);
271-
assert_eq!(recovered_struct.capacity, 2);
272-
273-
// Verify field types are preserved.
274-
// The first two fields were converted to mutable then frozen back.
275-
assert!(matches!(recovered_struct.fields[0], Vector::Null(_)));
276-
assert!(matches!(recovered_struct.fields[1], Vector::Primitive(_)));
277-
assert!(matches!(recovered_struct.fields[2], Vector::Bool(_)));
278-
}
279-
280-
#[test]
281-
fn test_try_into_mut_nested_struct() {
282-
// Create a nested struct: a `StructVector` containing other `StructVector`s as fields.
283-
let inner_struct1: Vector = create_struct_vector(
284-
vec![
285-
NullVector::new(3).into(),
286-
BoolVectorMut::from_iter([true, false, true])
287-
.freeze()
288-
.into(),
289-
],
290-
3,
291-
3,
292-
)
293-
.into();
294-
295-
let inner_struct2: Vector = create_struct_vector(
296-
vec![PVectorMut::<i32>::from_iter([1, 2, 3]).freeze().into()],
297-
3,
298-
3,
299-
)
300-
.into();
301-
302-
let outer_struct = create_struct_vector(vec![inner_struct1, inner_struct2], 3, 3);
303-
304-
// Attempt to convert to mutable.
305-
let result = outer_struct.try_into_mut();
306-
307-
// Should succeed once `StructVectorMut` is fully implemented.
308-
assert!(result.is_ok());
309-
310-
let mut_struct = result.unwrap();
311-
assert_eq!(mut_struct.len(), 3);
312-
assert_eq!(mut_struct.fields.len(), 2);
313-
314-
// Verify nested structs are converted correctly.
315-
assert!(matches!(mut_struct.fields[0], VectorMut::Struct(_)));
316-
assert!(matches!(mut_struct.fields[1], VectorMut::Struct(_)));
317-
}
318-
}

0 commit comments

Comments
 (0)