1use std::any::Any;
5
6use itertools::Itertools;
7use vortex_buffer::BufferAllocatorRef;
8use vortex_error::VortexExpect;
9use vortex_error::VortexResult;
10use vortex_error::vortex_bail;
11use vortex_error::vortex_ensure_eq;
12use vortex_error::vortex_panic;
13
14use crate::ArrayRef;
15use crate::ExecutionCtx;
16use crate::IntoArray;
17use crate::arrays::StructArray;
18use crate::arrays::struct_::StructArrayExt;
19use crate::builders::ArrayBuilder;
20use crate::builders::ChildBuilder;
21use crate::builders::DEFAULT_BUILDER_CAPACITY;
22use crate::builders::ValidityBuilder;
23use crate::canonical::Canonical;
24use crate::dtype::DType;
25use crate::dtype::Nullability;
26use crate::dtype::StructFields;
27use crate::scalar::Scalar;
28use crate::scalar::StructScalar;
29
30pub struct StructBuilder {
32 dtype: DType,
33 builders: Vec<ChildBuilder>,
34 nulls: ValidityBuilder,
35}
36
37impl StructBuilder {
38 #[deprecated(note = "use `new_in` with an explicit allocator")]
40 pub fn new(struct_dtype: StructFields, nullability: Nullability) -> Self {
41 Self::new_in(struct_dtype, nullability, BufferAllocatorRef::static_ref())
42 }
43
44 pub fn new_in(
46 struct_dtype: StructFields,
47 nullability: Nullability,
48 allocator: &BufferAllocatorRef,
49 ) -> Self {
50 Self::with_capacity_in(
51 struct_dtype,
52 nullability,
53 DEFAULT_BUILDER_CAPACITY,
54 allocator,
55 )
56 }
57
58 #[deprecated(note = "use `with_capacity_in` with an explicit allocator")]
60 pub fn with_capacity(
61 struct_dtype: StructFields,
62 nullability: Nullability,
63 capacity: usize,
64 ) -> Self {
65 Self::with_capacity_in(
66 struct_dtype,
67 nullability,
68 capacity,
69 BufferAllocatorRef::static_ref(),
70 )
71 }
72
73 pub fn with_capacity_in(
75 struct_dtype: StructFields,
76 nullability: Nullability,
77 capacity: usize,
78 allocator: &BufferAllocatorRef,
79 ) -> Self {
80 let builders = struct_dtype
81 .fields()
82 .map(|dt| ChildBuilder::with_capacity(&dt, capacity, allocator))
83 .collect();
84
85 Self {
86 builders,
87 nulls: ValidityBuilder::new(capacity, allocator),
88 dtype: DType::Struct(struct_dtype, nullability),
89 }
90 }
91
92 pub fn append_value(&mut self, struct_scalar: StructScalar) -> VortexResult<()> {
94 if !self.dtype.is_nullable() && struct_scalar.is_null() {
95 vortex_bail!("Tried to append a null `StructScalar` to a non-nullable struct builder",);
96 }
97
98 if struct_scalar.struct_fields() != self.struct_fields() {
99 vortex_bail!(
100 "Tried to append a `StructScalar` with fields {} to a \
101 struct builder with fields {}",
102 struct_scalar.struct_fields(),
103 self.struct_fields()
104 );
105 }
106
107 if let Some(fields) = struct_scalar.fields_iter() {
108 for (builder, field) in self.builders.iter_mut().zip_eq(fields) {
109 builder.append_scalar(&field)?;
110 }
111 self.nulls.append_non_null();
112 } else {
113 self.append_null()
114 }
115
116 Ok(())
117 }
118
119 pub fn finish_into_struct(&mut self) -> StructArray {
121 let len = self.len();
122 let fields = self
123 .builders
124 .iter_mut()
125 .map(|builder| builder.finish())
126 .collect::<Vec<_>>();
127
128 if fields.len() > 1 {
129 let expected_length = fields[0].len();
130 for (index, field) in fields[1..].iter().enumerate() {
131 assert_eq!(
132 field.len(),
133 expected_length,
134 "Field {index} does not have expected length {expected_length}"
135 );
136 }
137 }
138
139 let validity = self.nulls.finish_with_nullability(self.dtype.nullability());
140
141 StructArray::try_new_with_dtype(fields, self.struct_fields().clone(), len, validity)
142 .vortex_expect("Fields must all have same length.")
143 }
144
145 pub fn struct_fields(&self) -> &StructFields {
147 let DType::Struct(struct_fields, _) = &self.dtype else {
148 vortex_panic!("`StructBuilder` somehow had dtype {}", self.dtype);
149 };
150
151 struct_fields
152 }
153
154 pub(crate) fn append_struct_array(
157 &mut self,
158 array: &StructArray,
159 ctx: &mut ExecutionCtx,
160 ) -> VortexResult<()> {
161 for (field, builder) in array
162 .iter_unmasked_fields()
163 .zip_eq(self.builders.iter_mut())
164 {
165 builder.append_array(field, ctx)?;
166 }
167
168 self.nulls.append_validity(array.validity()?, array.len());
169 Ok(())
170 }
171}
172
173impl ArrayBuilder for StructBuilder {
174 fn as_any(&self) -> &dyn Any {
175 self
176 }
177
178 fn as_any_mut(&mut self) -> &mut dyn Any {
179 self
180 }
181
182 fn dtype(&self) -> &DType {
183 &self.dtype
184 }
185
186 fn len(&self) -> usize {
187 self.nulls.len()
188 }
189
190 fn append_zeros(&mut self, n: usize) {
191 self.builders
192 .iter_mut()
193 .for_each(|builder| builder.append_zeros(n));
194 self.nulls.append_n_non_nulls(n);
195 }
196
197 unsafe fn append_nulls_unchecked(&mut self, n: usize) {
198 self.builders
199 .iter_mut()
200 .for_each(|builder| builder.append_defaults(n));
203 self.nulls.append_n_nulls(n);
204 }
205
206 fn append_scalar(&mut self, scalar: &Scalar) -> VortexResult<()> {
207 vortex_ensure_eq!(
208 scalar.dtype(),
209 self.dtype(),
210 "StructBuilder received a scalar with the wrong dtype"
211 );
212
213 self.append_value(scalar.as_struct())
214 }
215
216 fn reserve_exact(&mut self, capacity: usize) {
217 self.builders.iter_mut().for_each(|builder| {
218 builder.reserve_exact(capacity);
219 });
220 self.nulls.reserve_exact(capacity);
221 }
222
223 fn finish(&mut self) -> ArrayRef {
224 self.finish_into_struct().into_array()
225 }
226
227 fn finish_into_canonical(&mut self, _ctx: &mut ExecutionCtx) -> Canonical {
228 Canonical::Struct(self.finish_into_struct())
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use vortex_buffer::BufferAllocatorRef;
235
236 use crate::IntoArray;
237 use crate::VortexSessionExecute;
238 use crate::array_session;
239 use crate::arrays::PrimitiveArray;
240 use crate::arrays::VarBinArray;
241 use crate::assert_arrays_eq;
242 use crate::builders::ArrayBuilder;
243 use crate::builders::struct_::StructArray;
244 use crate::builders::struct_::StructBuilder;
245 use crate::dtype::DType;
246 use crate::dtype::Nullability;
247 use crate::dtype::PType::I32;
248 use crate::dtype::StructFields;
249 use crate::scalar::Scalar;
250 use crate::validity::Validity;
251
252 #[test]
253 fn test_struct_builder() {
254 let sdt = StructFields::new(["a", "b"].into(), vec![I32.into(), I32.into()]);
255 let dtype = DType::Struct(sdt.clone(), Nullability::NonNullable);
256 let mut builder = StructBuilder::with_capacity_in(
257 sdt,
258 Nullability::NonNullable,
259 0,
260 BufferAllocatorRef::static_ref(),
261 );
262
263 builder
264 .append_value(Scalar::struct_(dtype.clone(), vec![1.into(), 2.into()]).as_struct())
265 .unwrap();
266
267 let struct_ = builder.finish();
268 assert_eq!(struct_.len(), 1);
269 assert_eq!(struct_.dtype(), &dtype);
270 assert_eq!(
271 struct_
272 .execute_scalar(0, &mut array_session().create_execution_ctx())
273 .unwrap(),
274 Scalar::struct_(dtype, vec![1i32.into(), 2i32.into()])
275 );
276 }
277
278 #[test]
279 fn test_append_nullable_struct() {
280 let sdt = StructFields::new(["a", "b"].into(), vec![I32.into(), I32.into()]);
281 let dtype = DType::Struct(sdt.clone(), Nullability::Nullable);
282 let mut builder = StructBuilder::with_capacity_in(
283 sdt,
284 Nullability::Nullable,
285 0,
286 BufferAllocatorRef::static_ref(),
287 );
288
289 builder
290 .append_value(Scalar::struct_(dtype.clone(), vec![1.into(), 2.into()]).as_struct())
291 .unwrap();
292
293 builder.append_nulls(2);
294
295 let struct_ = builder.finish();
296 assert_eq!(struct_.len(), 3);
297 assert_eq!(struct_.dtype(), &dtype);
298 assert_eq!(
299 struct_
300 .valid_count(&mut array_session().create_execution_ctx())
301 .unwrap(),
302 1
303 );
304 let mut ctx = array_session().create_execution_ctx();
305 assert_eq!(
306 struct_.execute_scalar(0, &mut ctx).unwrap(),
307 Scalar::struct_(dtype.clone(), vec![1i32.into(), 2i32.into()])
308 );
309 assert_eq!(
310 struct_.execute_scalar(1, &mut ctx).unwrap(),
311 Scalar::null(dtype.clone())
312 );
313 assert_eq!(
314 struct_.execute_scalar(2, &mut ctx).unwrap(),
315 Scalar::null(dtype)
316 );
317 }
318
319 #[test]
320 fn test_append_scalar() {
321 let mut ctx = array_session().create_execution_ctx();
322 use crate::scalar::Scalar;
323
324 let dtype = DType::Struct(
325 StructFields::from_iter([
326 ("a", DType::Primitive(I32, Nullability::Nullable)),
327 ("b", DType::Utf8(Nullability::Nullable)),
328 ]),
329 Nullability::Nullable,
330 );
331
332 let struct_fields = match &dtype {
333 DType::Struct(fields, _) => fields.clone(),
334 _ => panic!("Expected struct dtype"),
335 };
336 let mut builder = StructBuilder::new_in(
337 struct_fields,
338 Nullability::Nullable,
339 BufferAllocatorRef::static_ref(),
340 );
341
342 let struct_scalar1 = Scalar::struct_(
344 dtype.clone(),
345 vec![
346 Scalar::primitive(42i32, Nullability::Nullable),
347 Scalar::utf8("hello", Nullability::Nullable),
348 ],
349 );
350 builder.append_scalar(&struct_scalar1).unwrap();
351
352 let struct_scalar2 = Scalar::struct_(
354 dtype.clone(),
355 vec![
356 Scalar::primitive(84i32, Nullability::Nullable),
357 Scalar::utf8("world", Nullability::Nullable),
358 ],
359 );
360 builder.append_scalar(&struct_scalar2).unwrap();
361
362 let null_scalar = Scalar::null(dtype.clone());
364 builder.append_scalar(&null_scalar).unwrap();
365
366 let array = builder.finish_into_struct();
367
368 let expected = StructArray::try_from_iter_with_validity(
369 [
370 (
371 "a",
372 PrimitiveArray::from_option_iter([Some(42i32), Some(84), Some(123)])
373 .into_array(),
374 ),
375 (
376 "b",
377 <VarBinArray as FromIterator<_>>::from_iter([
378 Some("hello"),
379 Some("world"),
380 Some("x"),
381 ])
382 .into_array(),
383 ),
384 ],
385 Validity::from_iter([true, true, false]),
386 )
387 .unwrap();
388 assert_arrays_eq!(&array, &expected, &mut ctx);
389
390 let struct_fields = match &dtype {
392 DType::Struct(fields, _) => fields.clone(),
393 _ => panic!("Expected struct dtype"),
394 };
395 let mut builder = StructBuilder::new_in(
396 struct_fields,
397 Nullability::NonNullable,
398 BufferAllocatorRef::static_ref(),
399 );
400 let wrong_scalar = Scalar::from(42i32);
401 assert!(builder.append_scalar(&wrong_scalar).is_err());
402 }
403}