1use std::{
5 collections::{BinaryHeap, VecDeque},
6 ops::Range,
7 sync::Arc,
8};
9
10use super::{
11 fixed_size_list::StructuralFixedSizeListDecoder, list::StructuralListDecoder,
12 map::StructuralMapDecoder, primitive::StructuralPrimitiveFieldDecoder,
13};
14use crate::{
15 decoder::{
16 DecodedArray, FilterExpression, LoadedPageShard, NextDecodeTask, PageEncoding,
17 ScheduledScanLine, SchedulerContext, StructuralDecodeArrayTask, StructuralFieldDecoder,
18 StructuralFieldScheduler, StructuralSchedulingJob,
19 },
20 encoder::{EncodeTask, EncodedColumn, EncodedPage, FieldEncoder, OutOfLineBuffers},
21 format::pb,
22 repdef::{CompositeRepDefUnraveler, RepDefBuilder},
23};
24use arrow_array::{Array, ArrayRef, StructArray, cast::AsArray};
25use arrow_schema::{DataType, Fields};
26use futures::{
27 FutureExt, StreamExt, TryStreamExt,
28 future::BoxFuture,
29 stream::{FuturesOrdered, FuturesUnordered},
30};
31use itertools::Itertools;
32use lance_arrow::FieldExt;
33use lance_arrow::{deepcopy::deep_copy_nulls, r#struct::StructArrayExt};
34use lance_core::{Error, Result, datatypes::validate_fixed_size_list_dimensions};
35use log::trace;
36
37#[derive(Debug)]
38struct StructuralSchedulingJobWithStatus<'a> {
39 col_idx: u32,
40 col_name: &'a str,
41 job: Box<dyn StructuralSchedulingJob + 'a>,
42 rows_scheduled: u64,
43 rows_remaining: u64,
44 ready_scan_lines: VecDeque<ScheduledScanLine>,
45}
46
47impl PartialEq for StructuralSchedulingJobWithStatus<'_> {
48 fn eq(&self, other: &Self) -> bool {
49 self.col_idx == other.col_idx
50 }
51}
52
53impl Eq for StructuralSchedulingJobWithStatus<'_> {}
54
55impl PartialOrd for StructuralSchedulingJobWithStatus<'_> {
56 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
57 Some(self.cmp(other))
58 }
59}
60
61impl Ord for StructuralSchedulingJobWithStatus<'_> {
62 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
63 other.rows_scheduled.cmp(&self.rows_scheduled)
65 }
66}
67
68#[derive(Debug)]
75struct RepDefStructSchedulingJob<'a> {
76 children: BinaryHeap<StructuralSchedulingJobWithStatus<'a>>,
78 rows_scheduled: u64,
79 num_rows: u64,
80}
81
82impl<'a> RepDefStructSchedulingJob<'a> {
83 fn new(
84 scheduler: &'a StructuralStructScheduler,
85 children: Vec<Box<dyn StructuralSchedulingJob + 'a>>,
86 num_rows: u64,
87 ) -> Self {
88 let children = children
89 .into_iter()
90 .enumerate()
91 .map(|(idx, job)| StructuralSchedulingJobWithStatus {
92 col_idx: idx as u32,
93 col_name: scheduler.child_fields[idx].name(),
94 job,
95 rows_scheduled: 0,
96 rows_remaining: num_rows,
97 ready_scan_lines: VecDeque::new(),
98 })
99 .collect::<BinaryHeap<_>>();
100 Self {
101 children,
102 rows_scheduled: 0,
103 num_rows,
104 }
105 }
106}
107
108impl StructuralSchedulingJob for RepDefStructSchedulingJob<'_> {
109 fn schedule_next(
110 &mut self,
111 mut context: &mut SchedulerContext,
112 ) -> Result<Vec<ScheduledScanLine>> {
113 if self.children.is_empty() {
114 if self.rows_scheduled == self.num_rows {
116 return Ok(Vec::new());
117 }
118 self.rows_scheduled = self.num_rows;
119 return Ok(vec![ScheduledScanLine {
120 decoders: Vec::new(),
121 rows_scheduled: self.num_rows,
122 }]);
123 }
124
125 let mut decoders = Vec::new();
126 let old_rows_scheduled = self.rows_scheduled;
127 while old_rows_scheduled == self.rows_scheduled {
130 if self.children.is_empty() {
131 return Ok(Vec::new());
133 }
134 let mut next_child = self.children.pop().unwrap();
135 if next_child.ready_scan_lines.is_empty() {
136 let scoped = context.push(next_child.col_name, next_child.col_idx);
137 let child_scans = next_child.job.schedule_next(scoped.context)?;
138 context = scoped.pop();
139 if child_scans.is_empty() {
140 continue;
142 }
143 next_child.ready_scan_lines.extend(child_scans);
144 }
145 let child_scan = next_child.ready_scan_lines.pop_front().unwrap();
146 trace!(
147 "Scheduled {} rows for child {}",
148 child_scan.rows_scheduled, next_child.col_idx
149 );
150 next_child.rows_scheduled += child_scan.rows_scheduled;
151 next_child.rows_remaining -= child_scan.rows_scheduled;
152 decoders.extend(child_scan.decoders);
153 self.children.push(next_child);
154 self.rows_scheduled = self.children.peek().unwrap().rows_scheduled;
155 }
156 let struct_rows_scheduled = self.rows_scheduled - old_rows_scheduled;
157 Ok(vec![ScheduledScanLine {
158 decoders,
159 rows_scheduled: struct_rows_scheduled,
160 }])
161 }
162}
163
164#[derive(Debug)]
175pub struct StructuralStructScheduler {
176 children: Vec<Box<dyn StructuralFieldScheduler>>,
177 child_fields: Fields,
178}
179
180impl StructuralStructScheduler {
181 pub fn new(children: Vec<Box<dyn StructuralFieldScheduler>>, child_fields: Fields) -> Self {
182 Self {
183 children,
184 child_fields,
185 }
186 }
187}
188
189impl StructuralFieldScheduler for StructuralStructScheduler {
190 fn schedule_ranges<'a>(
191 &'a self,
192 ranges: &[Range<u64>],
193 filter: &FilterExpression,
194 ) -> Result<Box<dyn StructuralSchedulingJob + 'a>> {
195 let num_rows = ranges.iter().map(|r| r.end - r.start).sum();
196
197 let child_schedulers = self
198 .children
199 .iter()
200 .map(|child| child.schedule_ranges(ranges, filter))
201 .collect::<Result<Vec<_>>>()?;
202
203 Ok(Box::new(RepDefStructSchedulingJob::new(
204 self,
205 child_schedulers,
206 num_rows,
207 )))
208 }
209
210 fn initialize<'a>(
211 &'a mut self,
212 requested_ranges: Option<&'a [Range<u64>]>,
213 filter: &'a FilterExpression,
214 context: &'a SchedulerContext,
215 ) -> BoxFuture<'a, Result<()>> {
216 let children_initialization = self
219 .children
220 .iter_mut()
221 .map(|child| child.initialize(requested_ranges, filter, context))
222 .collect::<FuturesUnordered<_>>();
223 async move {
224 children_initialization
225 .map(|res| res.map(|_| ()))
226 .try_collect::<Vec<_>>()
227 .await?;
228 Ok(())
229 }
230 .boxed()
231 }
232}
233
234#[derive(Debug)]
235pub struct StructuralStructDecoder {
236 children: Vec<Box<dyn StructuralFieldDecoder>>,
237 data_type: DataType,
238 child_fields: Fields,
239 is_root: bool,
241 nullable: bool,
242}
243
244impl StructuralStructDecoder {
245 pub fn new(
246 fields: Fields,
247 should_validate: bool,
248 is_root: bool,
249 nullable: bool,
250 ) -> Result<Self> {
251 let children = fields
252 .iter()
253 .map(|field| Self::field_to_decoder(field, should_validate))
254 .collect::<Result<Vec<_>>>()?;
255 let data_type = DataType::Struct(fields.clone());
256 Ok(Self {
257 data_type,
258 children,
259 child_fields: fields,
260 is_root,
261 nullable: !is_root && nullable,
262 })
263 }
264
265 fn field_to_decoder(
266 field: &Arc<arrow_schema::Field>,
267 should_validate: bool,
268 ) -> Result<Box<dyn StructuralFieldDecoder>> {
269 match field.data_type() {
270 DataType::Struct(fields) => {
271 if field.is_packed_struct() || field.is_blob() {
272 let decoder =
273 StructuralPrimitiveFieldDecoder::new(&field.clone(), should_validate);
274 Ok(Box::new(decoder))
275 } else {
276 Ok(Box::new(Self::new(
277 fields.clone(),
278 should_validate,
279 false,
280 field.is_nullable(),
281 )?))
282 }
283 }
284 DataType::List(child_field) | DataType::LargeList(child_field) => {
285 let child_decoder = Self::field_to_decoder(child_field, should_validate)?;
286 Ok(Box::new(StructuralListDecoder::new(
287 child_decoder,
288 field.data_type().clone(),
289 )))
290 }
291 DataType::FixedSizeList(child_field, _)
292 if matches!(child_field.data_type(), DataType::Struct(_)) =>
293 {
294 validate_fixed_size_list_dimensions(field.name(), field.data_type())?;
300 let child_decoder = Self::field_to_decoder(child_field, should_validate)?;
302 Ok(Box::new(StructuralFixedSizeListDecoder::new(
303 child_decoder,
304 field.data_type().clone(),
305 )))
306 }
307 DataType::Map(entries_field, keys_sorted) => {
308 if *keys_sorted {
309 return Err(Error::not_supported_source(
310 "Map data type with keys_sorted=true is not supported yet"
311 .to_string()
312 .into(),
313 ));
314 }
315 let child_decoder = Self::field_to_decoder(entries_field, should_validate)?;
316 Ok(Box::new(StructuralMapDecoder::new(
317 child_decoder,
318 field.data_type().clone(),
319 )))
320 }
321 DataType::RunEndEncoded(_, _) => todo!(),
322 DataType::ListView(_) | DataType::LargeListView(_) => todo!(),
323 DataType::Union(_, _) => todo!(),
324 _ => Ok(Box::new(StructuralPrimitiveFieldDecoder::new(
325 field,
326 should_validate,
327 ))),
328 }
329 }
330
331 pub fn drain_batch_task(&mut self, num_rows: u64) -> Result<NextDecodeTask> {
332 let array_drain = self.drain(num_rows)?;
333 Ok(NextDecodeTask {
334 num_rows,
335 task: Box::new(array_drain),
336 })
337 }
338}
339
340impl StructuralFieldDecoder for StructuralStructDecoder {
341 fn accept_page(&mut self, mut child: LoadedPageShard) -> Result<()> {
342 let child_idx = child.path.pop_front().unwrap();
344 self.children[child_idx as usize].accept_page(child)?;
346 Ok(())
347 }
348
349 fn drain(&mut self, num_rows: u64) -> Result<Box<dyn StructuralDecodeArrayTask>> {
350 let child_tasks = self
351 .children
352 .iter_mut()
353 .map(|child| child.drain(num_rows))
354 .collect::<Result<Vec<_>>>()?;
355 Ok(Box::new(RepDefStructDecodeTask {
356 children: child_tasks,
357 child_fields: self.child_fields.clone(),
358 is_root: self.is_root,
359 num_rows,
360 }))
361 }
362
363 fn data_type(&self) -> &DataType {
364 &self.data_type
365 }
366
367 fn plan_decoded_bytes(&self, rows_remaining: u64) -> lance_core::Result<[u64; 8]> {
368 use crate::decoder::CANDIDATE_BATCH_SIZES;
369 let mut out = [0u64; 8];
370 for child in &self.children {
371 let child_bytes = child.plan_decoded_bytes(rows_remaining)?;
372 for (o, c) in out.iter_mut().zip(child_bytes) {
373 *o += c;
374 }
375 }
376 if self.nullable {
377 for (i, &candidate) in CANDIDATE_BATCH_SIZES.iter().enumerate() {
378 let rows = (candidate as u64).min(rows_remaining);
379 out[i] += rows.div_ceil(8);
380 }
381 }
382 Ok(out)
383 }
384}
385
386#[derive(Debug)]
387struct RepDefStructDecodeTask {
388 children: Vec<Box<dyn StructuralDecodeArrayTask>>,
389 child_fields: Fields,
390 is_root: bool,
391 num_rows: u64,
392}
393
394impl StructuralDecodeArrayTask for RepDefStructDecodeTask {
395 fn decode(self: Box<Self>) -> Result<DecodedArray> {
396 if self.children.is_empty() {
397 return Ok(DecodedArray {
398 array: Arc::new(StructArray::new_empty_fields(self.num_rows as usize, None)),
399 repdef: CompositeRepDefUnraveler::new(vec![]),
400 data_size: 0,
401 });
402 }
403
404 let arrays = self
405 .children
406 .into_iter()
407 .map(|task| task.decode())
408 .collect::<Result<Vec<_>>>()?;
409 let mut children = Vec::with_capacity(arrays.len());
410 let mut repdefs = Vec::with_capacity(arrays.len());
411 let mut data_size = 0u64;
412 let mut arrays_iter = arrays.into_iter();
413 let first_array = arrays_iter.next().ok_or_else(|| {
414 Error::internal("Struct decoder unexpectedly has no child arrays".to_string())
415 })?;
416 let length = first_array.array.len();
417
418 repdefs.push(first_array.repdef);
420 data_size += first_array.data_size;
421 children.push(first_array.array);
422
423 for array in arrays_iter {
424 if length != array.array.len() {
425 return Err(Error::invalid_input_source(
426 format!(
427 "Struct child array length {} does not match sibling length {}",
428 array.array.len(),
429 length
430 )
431 .into(),
432 ));
433 }
434 data_size += array.data_size;
435 children.push(array.array);
436 repdefs.push(array.repdef);
437 }
438
439 let primary_repdef = repdefs
444 .iter()
445 .position(CompositeRepDefUnraveler::has_sparse)
446 .unwrap_or(0);
447 let mut repdef = repdefs.swap_remove(primary_repdef);
448 if repdef.has_sparse() {
449 for sibling in repdefs {
450 if sibling.has_sparse() {
451 repdef.add_compatibility_check(sibling);
452 }
453 }
454 }
455
456 let validity = if self.is_root {
457 repdef.ensure_exhausted()?;
458 None
459 } else {
460 repdef.unravel_validity(length)?
461 };
462
463 let array = StructArray::try_new(self.child_fields, children, validity)
464 .map_err(|e| Error::invalid_input_source(e.to_string().into()))?;
465 Ok(DecodedArray {
466 array: Arc::new(array),
467 repdef,
468 data_size,
469 })
470 }
471}
472
473pub struct StructStructuralEncoder {
478 keep_original_array: bool,
479 children: Vec<Box<dyn FieldEncoder>>,
480}
481
482impl StructStructuralEncoder {
483 pub fn new(keep_original_array: bool, children: Vec<Box<dyn FieldEncoder>>) -> Self {
484 Self {
485 keep_original_array,
486 children,
487 }
488 }
489}
490
491impl FieldEncoder for StructStructuralEncoder {
492 fn maybe_encode(
493 &mut self,
494 array: ArrayRef,
495 external_buffers: &mut OutOfLineBuffers,
496 mut repdef: RepDefBuilder,
497 row_number: u64,
498 num_rows: u64,
499 ) -> Result<Vec<EncodeTask>> {
500 let struct_array = array.as_struct();
501 let mut struct_array = struct_array.normalize_slicing()?;
502 if let Some(validity) = struct_array.nulls() {
503 if self.keep_original_array {
504 repdef.add_validity_bitmap(validity.clone())
505 } else {
506 repdef.add_validity_bitmap(deep_copy_nulls(Some(validity)).unwrap())
507 }
508 struct_array = struct_array.pushdown_nulls()?;
509 } else {
510 repdef.add_no_null(struct_array.len());
511 }
512 let child_tasks = self
513 .children
514 .iter_mut()
515 .zip(struct_array.columns().iter())
516 .map(|(encoder, arr)| {
517 encoder.maybe_encode(
518 arr.clone(),
519 external_buffers,
520 repdef.clone(),
521 row_number,
522 num_rows,
523 )
524 })
525 .collect::<Result<Vec<_>>>()?;
526 Ok(child_tasks.into_iter().flatten().collect::<Vec<_>>())
527 }
528
529 fn flush(&mut self, external_buffers: &mut OutOfLineBuffers) -> Result<Vec<EncodeTask>> {
530 self.children
531 .iter_mut()
532 .map(|encoder| encoder.flush(external_buffers))
533 .flatten_ok()
534 .collect::<Result<Vec<_>>>()
535 }
536
537 fn num_columns(&self) -> u32 {
538 self.children
539 .iter()
540 .map(|child| child.num_columns())
541 .sum::<u32>()
542 }
543
544 fn finish(
545 &mut self,
546 external_buffers: &mut OutOfLineBuffers,
547 ) -> BoxFuture<'_, Result<Vec<crate::encoder::EncodedColumn>>> {
548 let mut child_columns = self
549 .children
550 .iter_mut()
551 .map(|child| child.finish(external_buffers))
552 .collect::<FuturesOrdered<_>>();
553 async move {
554 let mut encoded_columns = Vec::with_capacity(child_columns.len());
555 while let Some(child_cols) = child_columns.next().await {
556 encoded_columns.extend(child_cols?);
557 }
558 Ok(encoded_columns)
559 }
560 .boxed()
561 }
562}
563
564pub struct StructFieldEncoder {
565 children: Vec<Box<dyn FieldEncoder>>,
566 column_index: u32,
567 num_rows_seen: u64,
568}
569
570impl StructFieldEncoder {
571 pub fn new(children: Vec<Box<dyn FieldEncoder>>, column_index: u32) -> Self {
572 Self {
573 children,
574 column_index,
575 num_rows_seen: 0,
576 }
577 }
578}
579
580impl FieldEncoder for StructFieldEncoder {
581 fn maybe_encode(
582 &mut self,
583 array: ArrayRef,
584 external_buffers: &mut OutOfLineBuffers,
585 repdef: RepDefBuilder,
586 row_number: u64,
587 num_rows: u64,
588 ) -> Result<Vec<EncodeTask>> {
589 let struct_array = array.as_struct();
590 self.num_rows_seen += array.len() as u64;
591 let child_tasks = self
592 .children
593 .iter_mut()
594 .zip(struct_array.columns().iter())
595 .map(|(encoder, arr)| {
596 encoder.maybe_encode(
597 arr.clone(),
598 external_buffers,
599 repdef.clone(),
600 row_number,
601 num_rows,
602 )
603 })
604 .collect::<Result<Vec<_>>>()?;
605 Ok(child_tasks.into_iter().flatten().collect::<Vec<_>>())
606 }
607
608 fn flush(&mut self, external_buffers: &mut OutOfLineBuffers) -> Result<Vec<EncodeTask>> {
609 let child_tasks = self
610 .children
611 .iter_mut()
612 .map(|encoder| encoder.flush(external_buffers))
613 .collect::<Result<Vec<_>>>()?;
614 Ok(child_tasks.into_iter().flatten().collect::<Vec<_>>())
615 }
616
617 fn num_columns(&self) -> u32 {
618 self.children
619 .iter()
620 .map(|child| child.num_columns())
621 .sum::<u32>()
622 + 1
623 }
624
625 fn finish(
626 &mut self,
627 external_buffers: &mut OutOfLineBuffers,
628 ) -> BoxFuture<'_, Result<Vec<crate::encoder::EncodedColumn>>> {
629 let mut child_columns = self
630 .children
631 .iter_mut()
632 .map(|child| child.finish(external_buffers))
633 .collect::<FuturesOrdered<_>>();
634 let num_rows_seen = self.num_rows_seen;
635 let column_index = self.column_index;
636 async move {
637 let mut columns = Vec::new();
638 let mut header = EncodedColumn::default();
640 header.final_pages.push(EncodedPage {
641 data: Vec::new(),
642 description: PageEncoding::Legacy(pb::ArrayEncoding {
643 array_encoding: Some(pb::array_encoding::ArrayEncoding::Struct(
644 pb::SimpleStruct {},
645 )),
646 }),
647 num_rows: num_rows_seen,
648 column_idx: column_index,
649 row_number: 0, });
651 columns.push(header);
652 while let Some(child_cols) = child_columns.next().await {
654 columns.extend(child_cols?);
655 }
656 Ok(columns)
657 }
658 .boxed()
659 }
660}
661
662#[cfg(test)]
663mod tests {
664
665 use std::{collections::HashMap, sync::Arc};
666
667 use arrow_array::{
668 Array, ArrayRef, Float64Array, Int32Array, Int64Array, ListArray, StringArray, StructArray,
669 builder::{Int32Builder, ListBuilder},
670 };
671 use arrow_buffer::{BooleanBuffer, NullBuffer, OffsetBuffer, ScalarBuffer};
672 use arrow_schema::{DataType, Field, Fields};
673
674 use super::StructuralStructDecoder;
675 use crate::constants::{STRUCTURAL_ENCODING_FULLZIP, STRUCTURAL_ENCODING_META_KEY};
676 use crate::decoder::StructuralFieldDecoder;
677 use crate::testing::{
678 TestCases, TestEncoding, check_basic_random_case, check_round_trip_encoding_of_data,
679 };
680
681 #[test]
682 fn test_zero_dimension_fsl_decoder_errors() {
683 let item_fields = Fields::from(vec![Field::new("x", DataType::Int32, true)]);
687 let fields = Fields::from(vec![Field::new(
688 "vecs",
689 DataType::FixedSizeList(
690 Arc::new(Field::new("item", DataType::Struct(item_fields), true)),
691 0,
692 ),
693 true,
694 )]);
695
696 let err = StructuralStructDecoder::new(
697 fields, false, true, false,
698 )
699 .unwrap_err();
700 assert!(matches!(err, lance_core::Error::Schema { .. }));
701 assert!(
702 err.to_string()
703 .contains("dimension must be a positive integer"),
704 "unexpected error: {}",
705 err
706 );
707 }
708
709 #[rstest::rstest]
710 #[test_log::test(tokio::test)]
711 async fn test_simple_struct(
712 #[values(
713 TestEncoding::Array,
714 TestEncoding::StructuralU16,
715 TestEncoding::StructuralU32,
716 TestEncoding::StructuralSparse
717 )]
718 encoding: TestEncoding,
719 #[values(4096, 1024 * 1024)] page_size: u64,
720 #[values(false, true)] use_slicing: bool,
721 ) {
722 let data_type = DataType::Struct(Fields::from(vec![
723 Field::new("a", DataType::Int32, false),
724 Field::new("b", DataType::Int32, false),
725 ]));
726 let field = Field::new("", data_type, false);
727 check_basic_random_case(field, encoding, page_size, use_slicing).await;
728 }
729
730 #[test_log::test(tokio::test)]
731 async fn test_nullable_struct() {
732 let inner_fields = Fields::from(vec![
750 Field::new("x", DataType::Int32, false),
751 Field::new("y", DataType::Int32, true),
752 ]);
753 let inner_struct = DataType::Struct(inner_fields.clone());
754 let outer_fields = Fields::from(vec![
755 Field::new("score", DataType::Int32, true),
756 Field::new("location", inner_struct, true),
757 ]);
758
759 let x_vals = Int32Array::from(vec![Some(1), Some(2), Some(3), Some(4), Some(5)]);
760 let y_vals = Int32Array::from(vec![Some(6), None, Some(8), Some(9), Some(10)]);
761 let scores = Int32Array::from(vec![None, Some(12), Some(13), Some(14), Some(15)]);
762
763 let location_validity = NullBuffer::from(vec![true, true, true, false, true]);
764 let locations = StructArray::new(
765 inner_fields,
766 vec![Arc::new(x_vals), Arc::new(y_vals)],
767 Some(location_validity),
768 );
769
770 let rows_validity = NullBuffer::from(vec![true, true, true, true, false]);
771 let rows = StructArray::new(
772 outer_fields,
773 vec![Arc::new(scores), Arc::new(locations)],
774 Some(rows_validity),
775 );
776
777 let test_cases = TestCases::default().with_structural_encodings();
778
779 check_round_trip_encoding_of_data(vec![Arc::new(rows)], &test_cases, HashMap::new()).await;
780 }
781
782 #[test_log::test(tokio::test)]
783 async fn test_simple_masked_nonempty_list() {
784 let items = Int32Array::from(vec![Some(1), Some(2), None, Some(4), Some(5), Some(6)]);
787 let offsets = OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, 2, 3, 4, 4, 4, 5]));
788 let list_validity = BooleanBuffer::from(vec![true, true, true, true, false, true]);
789 let list_array = ListArray::new(
790 Arc::new(Field::new("item", DataType::Int32, true)),
791 offsets,
792 Arc::new(items),
793 Some(NullBuffer::new(list_validity)),
794 );
795 let struct_validity = BooleanBuffer::from(vec![true, true, true, true, true, false]);
796 let struct_array = StructArray::new(
797 Fields::from(vec![Field::new(
798 "inner_list",
799 list_array.data_type().clone(),
800 true,
801 )]),
802 vec![Arc::new(list_array)],
803 Some(NullBuffer::new(struct_validity)),
804 );
805 check_round_trip_encoding_of_data(
806 vec![Arc::new(struct_array)],
807 &TestCases::default().with_structural_encodings(),
808 HashMap::new(),
809 )
810 .await;
811 }
812
813 #[test_log::test(tokio::test)]
814 async fn test_simple_struct_list() {
815 let items = Int32Array::from(vec![Some(1), Some(2), None, Some(4)]);
818 let offsets = OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, 2, 3, 4, 4, 4, 4]));
819 let list_validity = BooleanBuffer::from(vec![true, true, true, true, false, true]);
820 let list_array = ListArray::new(
821 Arc::new(Field::new("item", DataType::Int32, true)),
822 offsets,
823 Arc::new(items),
824 Some(NullBuffer::new(list_validity)),
825 );
826 let struct_validity = BooleanBuffer::from(vec![true, true, true, true, true, false]);
827 let struct_array = StructArray::new(
828 Fields::from(vec![Field::new(
829 "inner_list",
830 list_array.data_type().clone(),
831 true,
832 )]),
833 vec![Arc::new(list_array)],
834 Some(NullBuffer::new(struct_validity)),
835 );
836 check_round_trip_encoding_of_data(
837 vec![Arc::new(struct_array)],
838 &TestCases::default().with_structural_encodings(),
839 HashMap::new(),
840 )
841 .await;
842 }
843
844 #[rstest::rstest]
845 #[test_log::test(tokio::test)]
846 async fn test_struct_list(
847 #[values(
848 TestEncoding::Array,
849 TestEncoding::StructuralU16,
850 TestEncoding::StructuralU32,
851 TestEncoding::StructuralSparse
852 )]
853 encoding: TestEncoding,
854 #[values(4096, 1024 * 1024)] page_size: u64,
855 #[values(false, true)] use_slicing: bool,
856 ) {
857 let data_type = DataType::Struct(Fields::from(vec![
858 Field::new(
859 "inner_list",
860 DataType::List(Arc::new(Field::new("item", DataType::Int32, true))),
861 true,
862 ),
863 Field::new("outer_int", DataType::Int32, true),
864 ]));
865 let field = Field::new("row", data_type, false);
866 check_basic_random_case(field, encoding, page_size, use_slicing).await;
867 }
868
869 #[rstest::rstest]
870 #[test_log::test(tokio::test)]
871 async fn test_empty_struct(
872 #[values(
873 TestEncoding::Array,
874 TestEncoding::StructuralU16,
875 TestEncoding::StructuralU32,
876 TestEncoding::StructuralSparse
877 )]
878 encoding: TestEncoding,
879 #[values(4096, 1024 * 1024)] page_size: u64,
880 #[values(false, true)] use_slicing: bool,
881 ) {
882 let data_type = DataType::Struct(Fields::from(Vec::<Field>::default()));
885 let field = Field::new("row", data_type, false);
886 check_basic_random_case(field, encoding, page_size, use_slicing).await;
887 }
888
889 #[rstest::rstest]
890 #[test_log::test(tokio::test)]
891 async fn test_complicated_struct(
892 #[values(
893 TestEncoding::Array,
894 TestEncoding::StructuralU16,
895 TestEncoding::StructuralU32,
896 TestEncoding::StructuralSparse
897 )]
898 encoding: TestEncoding,
899 #[values(4096, 1024 * 1024)] page_size: u64,
900 #[values(false, true)] use_slicing: bool,
901 ) {
902 let data_type = DataType::Struct(Fields::from(vec![
903 Field::new("int", DataType::Int32, true),
904 Field::new(
905 "inner",
906 DataType::Struct(Fields::from(vec![
907 Field::new("inner_int", DataType::Int32, true),
908 Field::new(
909 "inner_list",
910 DataType::List(Arc::new(Field::new("item", DataType::Int32, true))),
911 true,
912 ),
913 ])),
914 true,
915 ),
916 Field::new("outer_binary", DataType::Binary, true),
917 ]));
918 let field = Field::new("row", data_type, false);
919 check_basic_random_case(field, encoding, page_size, use_slicing).await;
920 }
921
922 #[test_log::test(tokio::test)]
923 async fn test_list_of_struct_with_null_struct_element() {
924 use arrow_array::StringArray;
927
928 let tag_array = StringArray::from(vec![
929 Some("valid"),
930 Some("null_struct"),
931 Some("valid"),
932 Some("valid"),
933 ]);
934 let struct_fields = Fields::from(vec![Field::new("tag", DataType::Utf8, true)]);
935 let struct_validity = NullBuffer::from(vec![false, true, false, false]);
937 let struct_array = StructArray::new(
938 struct_fields.clone(),
939 vec![Arc::new(tag_array)],
940 Some(struct_validity),
941 );
942
943 let offsets = OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, 4]));
944 let list_field = Field::new("item", DataType::Struct(struct_fields), true);
945 let list_array =
946 ListArray::new(Arc::new(list_field), offsets, Arc::new(struct_array), None);
947
948 check_round_trip_encoding_of_data(
949 vec![Arc::new(list_array)],
950 &TestCases::default().with_u32_structural_encodings(),
951 HashMap::new(),
952 )
953 .await;
954 }
955
956 #[test_log::test(tokio::test)]
957 async fn test_list_of_struct_with_all_null_child() {
958 use arrow_array::StringArray;
959
960 let a_array = StringArray::from(vec![Some("w"), Some("x"), Some("y"), Some("z")]);
961 let b_array = StringArray::from(vec![None::<&str>, None, None, None]);
962 let struct_fields = Fields::from(vec![
963 Field::new("a", DataType::Utf8, true),
964 Field::new("b", DataType::Utf8, true),
965 ]);
966 let struct_array = StructArray::new(
967 struct_fields.clone(),
968 vec![Arc::new(a_array), Arc::new(b_array)],
969 None,
970 );
971
972 let offsets = OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, 2, 4]));
973 let list_field = Field::new("item", DataType::Struct(struct_fields), true);
974 let list_array =
975 ListArray::new(Arc::new(list_field), offsets, Arc::new(struct_array), None);
976
977 check_round_trip_encoding_of_data(
978 vec![Arc::new(list_array)],
979 &TestCases::default()
980 .with_range(0..1)
981 .with_range(1..2)
982 .with_indices(vec![0])
983 .with_indices(vec![1])
984 .with_structural_encodings(),
985 HashMap::new(),
986 )
987 .await;
988 }
989
990 #[test_log::test(tokio::test)]
991 async fn test_list_of_struct_with_constant_child_and_empty_lists() {
992 let item_fields = Fields::from(vec![
993 Field::new("maneuver", DataType::Int64, true),
994 Field::new("remaining_dist", DataType::Float64, true),
995 ]);
996 let item_array = StructArray::new(
997 item_fields.clone(),
998 vec![
999 Arc::new(Int64Array::from(vec![Some(3), Some(3), Some(3)])),
1000 Arc::new(Float64Array::from(vec![Some(1.0), Some(2.0), Some(3.0)])),
1001 ],
1002 None,
1003 );
1004
1005 let list_field = Field::new("item", DataType::Struct(item_fields), true);
1006 let list_array = ListArray::new(
1007 Arc::new(list_field),
1008 OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, 1, 2, 3, 3, 3, 3])),
1009 Arc::new(item_array),
1010 None,
1011 );
1012
1013 let data_fields = Fields::from(vec![Field::new(
1014 "maneuvers",
1015 list_array.data_type().clone(),
1016 true,
1017 )]);
1018 let data_array = StructArray::new(data_fields.clone(), vec![Arc::new(list_array)], None);
1019 let row_validity = NullBuffer::from(vec![true, true, true, true, true, false]);
1020 let row_array = StructArray::new(
1021 Fields::from(vec![Field::new(
1022 "data",
1023 DataType::Struct(data_fields),
1024 true,
1025 )]),
1026 vec![Arc::new(data_array)],
1027 Some(row_validity),
1028 );
1029
1030 check_round_trip_encoding_of_data(
1031 vec![Arc::new(row_array)],
1032 &TestCases::default().with_structural_encodings(),
1033 HashMap::new(),
1034 )
1035 .await;
1036 }
1037
1038 #[test]
1039 fn test_plan_decoded_bytes_struct_non_nullable_primitive() {
1040 use arrow_buffer::NullBuffer;
1046
1047 const N: usize = 1024;
1048 let x_vals = Int32Array::from_iter_values(0..N as i32);
1049 let struct_nulls = NullBuffer::from((0..N).map(|i| i % 2 == 0).collect::<Vec<_>>());
1050 let struct_field = Field::new("x", DataType::Int32, false);
1051 let struct_array = StructArray::new(
1052 Fields::from(vec![struct_field]),
1053 vec![Arc::new(x_vals) as ArrayRef],
1054 Some(struct_nulls),
1055 );
1056 let expected = struct_array.get_buffer_memory_size() as u64;
1057
1058 let child_field = Arc::new(Field::new("x", DataType::Int32, false));
1059 let fields = Fields::from(vec![child_field]);
1060 let decoder = StructuralStructDecoder::new(
1061 fields, false, false, true,
1062 )
1063 .unwrap();
1064
1065 let bytes = decoder.plan_decoded_bytes(N as u64).unwrap();
1066 assert_eq!(
1068 bytes[5], expected,
1069 "plan_decoded_bytes({N}) = {} but get_buffer_memory_size = {expected}",
1070 bytes[5],
1071 );
1072 }
1073
1074 #[test]
1075 fn test_plan_decoded_bytes_struct_nullable_primitive() {
1076 use arrow_buffer::NullBuffer;
1079
1080 const N: usize = 1024;
1081 let y_vals =
1082 Int32Array::from_iter((0..N as i32).map(|i| if i % 2 == 0 { Some(i) } else { None }));
1083 let struct_nulls = NullBuffer::from((0..N).map(|i| i % 3 != 0).collect::<Vec<_>>());
1084 let struct_field = Field::new("y", DataType::Int32, true);
1085 let struct_array = StructArray::new(
1086 Fields::from(vec![struct_field]),
1087 vec![Arc::new(y_vals) as ArrayRef],
1088 Some(struct_nulls),
1089 );
1090 let expected = struct_array.get_buffer_memory_size() as u64;
1091
1092 let child_field = Arc::new(Field::new("y", DataType::Int32, true));
1093 let fields = Fields::from(vec![child_field]);
1094 let decoder = StructuralStructDecoder::new(
1095 fields, false, false, true,
1096 )
1097 .unwrap();
1098
1099 let bytes = decoder.plan_decoded_bytes(N as u64).unwrap();
1100 assert_eq!(
1101 bytes[5], expected,
1102 "plan_decoded_bytes({N}) = {} but get_buffer_memory_size = {expected}",
1103 bytes[5],
1104 );
1105 }
1106
1107 #[test_log::test(tokio::test)]
1108 async fn test_ragged_scheduling() {
1109 let items_builder = Int32Builder::new();
1113 let mut list_builder = ListBuilder::new(items_builder);
1114 for _ in 0..10000 {
1115 list_builder.append_null();
1116 }
1117 let list_array = Arc::new(list_builder.finish());
1118 let int_array = Arc::new(Int32Array::from_iter_values(0..10000));
1119 let fields = vec![
1120 Field::new("", list_array.data_type().clone(), true),
1121 Field::new("", int_array.data_type().clone(), true),
1122 ];
1123 let struct_array = Arc::new(StructArray::new(
1124 Fields::from(fields),
1125 vec![list_array, int_array],
1126 None,
1127 )) as ArrayRef;
1128 let struct_arrays = (0..10000)
1129 .step_by(437)
1131 .map(|offset| struct_array.slice(offset, 437.min(10000 - offset)))
1132 .collect::<Vec<_>>();
1133 check_round_trip_encoding_of_data(struct_arrays, &TestCases::default(), HashMap::new())
1134 .await;
1135 }
1136
1137 #[rstest::rstest]
1143 #[test_log::test(tokio::test)]
1144 async fn test_full_zip_invisible_items_under_nullable_struct(
1145 #[values(false, true)] variable_width_items: bool,
1146 ) {
1147 let (item_type, items): (DataType, ArrayRef) = if variable_width_items {
1150 (
1151 DataType::Utf8,
1152 Arc::new(StringArray::from(vec!["a", "b", "c", "d", "e"])),
1153 )
1154 } else {
1155 (
1156 DataType::Int32,
1157 Arc::new(Int32Array::from(vec![1, 2, 3, 4, 5])),
1158 )
1159 };
1160 let lists = ListArray::new(
1161 Arc::new(Field::new("item", item_type, true)),
1162 OffsetBuffer::new(ScalarBuffer::from(vec![0, 2, 2, 3, 3, 5])),
1163 items,
1164 Some(NullBuffer::from(vec![true, false, true, true, true])),
1165 );
1166 let fields = Fields::from(vec![Field::new("x", lists.data_type().clone(), true)]);
1167 let rows = StructArray::new(
1168 fields,
1169 vec![Arc::new(lists)],
1170 Some(NullBuffer::from(vec![true, true, true, false, true])),
1171 );
1172
1173 let metadata = HashMap::from([(
1174 STRUCTURAL_ENCODING_META_KEY.to_string(),
1175 STRUCTURAL_ENCODING_FULLZIP.to_string(),
1176 )]);
1177 let test_cases = TestCases::default().with_structural_encodings();
1178 check_round_trip_encoding_of_data(vec![Arc::new(rows)], &test_cases, metadata).await;
1179 }
1180}