1use std::{ops::Range, sync::Arc};
5
6use arrow_array::{Array, ArrayRef, LargeListArray, ListArray, cast::AsArray, make_array};
7use arrow_schema::DataType;
8use futures::future::BoxFuture;
9use lance_arrow::deepcopy::deep_copy_nulls;
10use lance_arrow::list::ListArrayExt;
11use lance_core::Result;
12
13use crate::{
14 decoder::{
15 DecodedArray, FilterExpression, ScheduledScanLine, SchedulerContext,
16 StructuralDecodeArrayTask, StructuralFieldDecoder, StructuralFieldScheduler,
17 StructuralSchedulingJob,
18 },
19 encoder::{EncodeTask, FieldEncoder, OutOfLineBuffers},
20 repdef::RepDefBuilder,
21};
22
23pub struct ListStructuralEncoder {
31 keep_original_array: bool,
32 child: Box<dyn FieldEncoder>,
33}
34
35impl ListStructuralEncoder {
36 pub fn new(keep_original_array: bool, child: Box<dyn FieldEncoder>) -> Self {
37 Self {
38 keep_original_array,
39 child,
40 }
41 }
42}
43
44impl FieldEncoder for ListStructuralEncoder {
45 fn maybe_encode(
46 &mut self,
47 array: ArrayRef,
48 external_buffers: &mut OutOfLineBuffers,
49 mut repdef: RepDefBuilder,
50 row_number: u64,
51 num_rows: u64,
52 ) -> Result<Vec<EncodeTask>> {
53 let values = if let Some(list_arr) = array.as_list_opt::<i32>() {
54 let has_garbage_values = if self.keep_original_array {
55 repdef.add_offsets(list_arr.offsets().clone(), array.nulls().cloned())
56 } else {
57 repdef.add_offsets(list_arr.offsets().clone(), deep_copy_nulls(array.nulls()))
59 };
60 if has_garbage_values {
61 list_arr.filter_garbage_nulls().trimmed_values()
62 } else {
63 list_arr.trimmed_values()
64 }
65 } else if let Some(list_arr) = array.as_list_opt::<i64>() {
66 let has_garbage_values = if self.keep_original_array {
67 repdef.add_offsets(list_arr.offsets().clone(), array.nulls().cloned())
68 } else {
69 repdef.add_offsets(list_arr.offsets().clone(), deep_copy_nulls(array.nulls()))
70 };
71 if has_garbage_values {
72 list_arr.filter_garbage_nulls().trimmed_values()
73 } else {
74 list_arr.trimmed_values()
75 }
76 } else {
77 panic!("List encoder used for non-list data")
78 };
79 self.child
80 .maybe_encode(values, external_buffers, repdef, row_number, num_rows)
81 }
82
83 fn flush(&mut self, external_buffers: &mut OutOfLineBuffers) -> Result<Vec<EncodeTask>> {
84 self.child.flush(external_buffers)
85 }
86
87 fn num_columns(&self) -> u32 {
88 self.child.num_columns()
89 }
90
91 fn finish(
92 &mut self,
93 external_buffers: &mut OutOfLineBuffers,
94 ) -> BoxFuture<'_, Result<Vec<crate::encoder::EncodedColumn>>> {
95 self.child.finish(external_buffers)
96 }
97}
98
99#[derive(Debug)]
100pub struct StructuralListScheduler {
101 child: Box<dyn StructuralFieldScheduler>,
102}
103
104impl StructuralListScheduler {
105 pub fn new(child: Box<dyn StructuralFieldScheduler>) -> Self {
106 Self { child }
107 }
108}
109
110impl StructuralFieldScheduler for StructuralListScheduler {
111 fn schedule_ranges<'a>(
112 &'a self,
113 ranges: &[Range<u64>],
114 filter: &FilterExpression,
115 ) -> Result<Box<dyn StructuralSchedulingJob + 'a>> {
116 let child = self.child.schedule_ranges(ranges, filter)?;
117
118 Ok(Box::new(StructuralListSchedulingJob::new(child)))
119 }
120
121 fn initialize<'a>(
122 &'a mut self,
123 filter: &'a FilterExpression,
124 context: &'a SchedulerContext,
125 ) -> BoxFuture<'a, Result<()>> {
126 self.child.initialize(filter, context)
127 }
128}
129
130#[derive(Debug)]
135struct StructuralListSchedulingJob<'a> {
136 child: Box<dyn StructuralSchedulingJob + 'a>,
137}
138
139impl<'a> StructuralListSchedulingJob<'a> {
140 fn new(child: Box<dyn StructuralSchedulingJob + 'a>) -> Self {
141 Self { child }
142 }
143}
144
145impl StructuralSchedulingJob for StructuralListSchedulingJob<'_> {
146 fn schedule_next(&mut self, context: &mut SchedulerContext) -> Result<Vec<ScheduledScanLine>> {
147 self.child.schedule_next(context)
148 }
149}
150
151#[derive(Debug)]
152pub struct StructuralListDecoder {
153 child: Box<dyn StructuralFieldDecoder>,
154 data_type: DataType,
155}
156
157impl StructuralListDecoder {
158 pub fn new(child: Box<dyn StructuralFieldDecoder>, data_type: DataType) -> Self {
159 Self { child, data_type }
160 }
161}
162
163impl StructuralFieldDecoder for StructuralListDecoder {
164 fn accept_page(&mut self, child: crate::decoder::LoadedPageShard) -> Result<()> {
165 self.child.accept_page(child)
166 }
167
168 fn drain(&mut self, num_rows: u64) -> Result<Box<dyn StructuralDecodeArrayTask>> {
169 let child_task = self.child.drain(num_rows)?;
170 Ok(Box::new(StructuralListDecodeTask::new(
171 child_task,
172 self.data_type.clone(),
173 )))
174 }
175
176 fn data_type(&self) -> &DataType {
177 &self.data_type
178 }
179}
180
181#[derive(Debug)]
182struct StructuralListDecodeTask {
183 child_task: Box<dyn StructuralDecodeArrayTask>,
184 data_type: DataType,
185}
186
187impl StructuralListDecodeTask {
188 fn new(child_task: Box<dyn StructuralDecodeArrayTask>, data_type: DataType) -> Self {
189 Self {
190 child_task,
191 data_type,
192 }
193 }
194}
195
196impl StructuralDecodeArrayTask for StructuralListDecodeTask {
197 fn decode(self: Box<Self>) -> Result<DecodedArray> {
198 let DecodedArray {
199 array,
200 mut repdef,
201 data_size,
202 } = self.child_task.decode()?;
203 match &self.data_type {
204 DataType::List(child_field) => {
205 let (offsets, validity) = repdef.unravel_offsets::<i32>()?;
206 let array = if !child_field.is_nullable() && array.null_count() == array.len() {
207 make_array(array.into_data().into_builder().nulls(None).build()?)
208 } else {
209 array
210 };
211 let list_array = ListArray::try_new(child_field.clone(), offsets, array, validity)?;
212
213 Ok(DecodedArray {
214 array: Arc::new(list_array),
215 repdef,
216 data_size,
217 })
218 }
219 DataType::LargeList(child_field) => {
220 let (offsets, validity) = repdef.unravel_offsets::<i64>()?;
221 let list_array =
222 LargeListArray::try_new(child_field.clone(), offsets, array, validity)?;
223 Ok(DecodedArray {
224 array: Arc::new(list_array),
225 repdef,
226 data_size,
227 })
228 }
229 _ => panic!("List decoder did not have a list field"),
230 }
231 }
232}
233
234#[cfg(test)]
235mod tests {
236
237 use std::{collections::HashMap, sync::Arc};
238
239 use crate::constants::{
240 STRUCTURAL_ENCODING_FULLZIP, STRUCTURAL_ENCODING_META_KEY, STRUCTURAL_ENCODING_MINIBLOCK,
241 };
242 use arrow_array::{
243 Array, ArrayRef, BooleanArray, DictionaryArray, LargeStringArray, ListArray, StructArray,
244 UInt8Array, UInt64Array,
245 builder::{
246 Int32Builder, Int64Builder, LargeListBuilder, ListBuilder, StringBuilder, UInt32Builder,
247 },
248 };
249
250 use arrow_buffer::{BooleanBuffer, NullBuffer, OffsetBuffer, ScalarBuffer};
251 use arrow_schema::{DataType, Field, Fields};
252 use rstest::rstest;
253
254 use crate::testing::{
255 TestCases, TestEncoding, check_basic_random, check_round_trip_encoding_of_data,
256 create_test_field_encoder, test_encoding_strategy,
257 };
258
259 fn make_list_type(inner_type: DataType) -> DataType {
260 DataType::List(Arc::new(Field::new("item", inner_type, true)))
261 }
262
263 fn make_large_list_type(inner_type: DataType) -> DataType {
264 DataType::LargeList(Arc::new(Field::new("item", inner_type, true)))
265 }
266
267 async fn try_encode_v22_pages(
268 array: ArrayRef,
269 ) -> lance_core::Result<Vec<crate::encoder::EncodedPage>> {
270 try_encode_v22_pages_with_metadata(array, HashMap::new()).await
271 }
272
273 async fn try_encode_v22_pages_with_metadata(
274 array: ArrayRef,
275 field_metadata: HashMap<String, String>,
276 ) -> lance_core::Result<Vec<crate::encoder::EncodedPage>> {
277 let arrow_field =
278 Field::new("", array.data_type().clone(), true).with_metadata(field_metadata);
279 let lance_field = lance_core::datatypes::Field::try_from(&arrow_field).unwrap();
280 let encoding_strategy = test_encoding_strategy(TestEncoding::StructuralU32);
281 let mut column_index_seq = crate::encoder::ColumnIndexSequence::default();
282 let encoding_options = crate::encoder::EncodingOptions::default();
283 let mut encoder = create_test_field_encoder(
284 encoding_strategy.as_ref(),
285 &lance_field,
286 &mut column_index_seq,
287 &encoding_options,
288 )
289 .unwrap();
290 let mut external_buffers =
291 crate::encoder::OutOfLineBuffers::new(0, crate::encoder::MIN_PAGE_BUFFER_ALIGNMENT);
292 let num_rows = array.len() as u64;
293 let mut pages = Vec::new();
294 for task in encoder
295 .maybe_encode(
296 array,
297 &mut external_buffers,
298 crate::repdef::RepDefBuilder::default(),
299 0,
300 num_rows,
301 )
302 .unwrap()
303 {
304 pages.push(task.await?);
305 }
306 for task in encoder.flush(&mut external_buffers).unwrap() {
307 pages.push(task.await?);
308 }
309 Ok(pages)
310 }
311
312 async fn encode_v22_pages(array: ArrayRef) -> Vec<crate::encoder::EncodedPage> {
313 try_encode_v22_pages(array).await.unwrap()
314 }
315
316 fn assert_split_miniblock_layout(
317 pages: &[crate::encoder::EncodedPage],
318 expect_structural_only_page: bool,
319 ) {
320 let mut miniblock_pages = 0;
321 let mut fullzip_pages = 0;
322 let mut structural_only_pages = 0;
323
324 for page in pages {
325 let crate::decoder::PageEncoding::Structural(layout) = &page.description else {
326 continue;
327 };
328 match layout.layout.as_ref().unwrap() {
329 crate::format::pb21::page_layout::Layout::MiniBlockLayout(_) => {
330 miniblock_pages += 1;
331 }
332 crate::format::pb21::page_layout::Layout::FullZipLayout(_) => {
333 fullzip_pages += 1;
334 }
335 crate::format::pb21::page_layout::Layout::ConstantLayout(layout) => {
336 if layout.inline_value.is_none()
337 && (layout.num_rep_values > 0 || layout.num_def_values > 0)
338 {
339 structural_only_pages += 1;
340 }
341 }
342 crate::format::pb21::page_layout::Layout::BlobLayout(_) => {}
343 crate::format::pb21::page_layout::Layout::SparseLayout(_) => {}
344 }
345 }
346
347 assert!(
348 miniblock_pages > 0,
349 "expected leaf values to remain on mini-block pages"
350 );
351 assert_eq!(
352 fullzip_pages, 0,
353 "split list pages should not fall back to full-zip"
354 );
355 if expect_structural_only_page {
356 assert!(
357 structural_only_pages > 0,
358 "expected at least one structural-only page"
359 );
360 }
361 }
362
363 fn assert_has_fullzip_layout(pages: &[crate::encoder::EncodedPage]) {
364 let has_fullzip = pages.iter().any(|page| {
365 let crate::decoder::PageEncoding::Structural(layout) = &page.description else {
366 return false;
367 };
368 matches!(
369 layout.layout.as_ref().unwrap(),
370 crate::format::pb21::page_layout::Layout::FullZipLayout(_)
371 )
372 });
373 assert!(has_fullzip, "expected at least one full-zip page");
374 }
375
376 #[rstest]
377 #[test_log::test(tokio::test)]
378 async fn test_list(
379 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
380 structural_encoding: &str,
381 ) {
382 let mut field_metadata = HashMap::new();
383 field_metadata.insert(
384 STRUCTURAL_ENCODING_META_KEY.to_string(),
385 structural_encoding.into(),
386 );
387 let field =
388 Field::new("", make_list_type(DataType::Int32), true).with_metadata(field_metadata);
389 check_basic_random(field).await;
390 }
391
392 #[rstest]
393 #[test_log::test(tokio::test)]
394 async fn test_deeply_nested_lists(
395 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
396 structural_encoding: &str,
397 ) {
398 let mut field_metadata = HashMap::new();
399 field_metadata.insert(
400 STRUCTURAL_ENCODING_META_KEY.to_string(),
401 structural_encoding.into(),
402 );
403 let field = Field::new("item", DataType::Int32, true).with_metadata(field_metadata);
404 for _ in 0..5 {
405 let field = Field::new("", make_list_type(field.data_type().clone()), true);
406 check_basic_random(field).await;
407 }
408 }
409
410 #[test_log::test(tokio::test)]
411 async fn test_large_list() {
412 let field = Field::new("", make_large_list_type(DataType::Int32), true);
413 check_basic_random(field).await;
414 }
415
416 #[test_log::test(tokio::test)]
417 async fn test_nested_strings() {
418 let field = Field::new("", make_list_type(DataType::Utf8), true);
419 check_basic_random(field).await;
420 }
421
422 #[test_log::test(tokio::test)]
423 async fn test_nested_list() {
424 let field = Field::new("", make_list_type(make_list_type(DataType::Int32)), true);
425 check_basic_random(field).await;
426 }
427
428 #[rstest]
447 #[test_log::test(tokio::test)]
448 async fn test_multipage_nested_float_list(
449 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
450 structural_encoding: &str,
451 ) {
452 use arrow_array::Float32Array;
453
454 let inner_per_row: usize = 3;
456 let inner_len: usize = 768;
457 let chunk_rows: &[usize] = &[1366, 1365];
461
462 let make_chunk = |start_row: usize, num_rows: usize| -> Arc<dyn Array> {
463 let total_inner = num_rows * inner_per_row;
464 let total_values = total_inner * inner_len;
465 let values = Float32Array::from(
466 (0..total_values)
467 .map(|i| (start_row + i) as f32)
468 .collect::<Vec<_>>(),
469 );
470 let inner_offsets = ScalarBuffer::<i32>::from(
471 (0..=total_inner)
472 .map(|i| (i * inner_len) as i32)
473 .collect::<Vec<_>>(),
474 );
475 let inner_list = ListArray::new(
476 Arc::new(Field::new("item", DataType::Float32, true)),
477 OffsetBuffer::new(inner_offsets),
478 Arc::new(values),
479 None,
480 );
481 let outer_offsets = ScalarBuffer::<i32>::from(
482 (0..=num_rows)
483 .map(|i| (i * inner_per_row) as i32)
484 .collect::<Vec<_>>(),
485 );
486 Arc::new(ListArray::new(
487 Arc::new(Field::new(
488 "item",
489 DataType::List(Arc::new(Field::new("item", DataType::Float32, true))),
490 true,
491 )),
492 OffsetBuffer::new(outer_offsets),
493 Arc::new(inner_list),
494 None,
495 ))
496 };
497
498 let mut start = 0;
499 let chunks: Vec<Arc<dyn Array>> = chunk_rows
500 .iter()
501 .map(|&n| {
502 let c = make_chunk(start, n);
503 start += n;
504 c
505 })
506 .collect();
507
508 let mut field_metadata = HashMap::new();
509 field_metadata.insert(
510 STRUCTURAL_ENCODING_META_KEY.to_string(),
511 structural_encoding.into(),
512 );
513
514 let test_cases = TestCases::default().with_structural_encodings();
515 check_round_trip_encoding_of_data(chunks, &test_cases, field_metadata).await;
516 }
517
518 #[test_log::test(tokio::test)]
519 async fn test_list_struct_list() {
520 let struct_type = DataType::Struct(Fields::from(vec![Field::new(
521 "inner_str",
522 DataType::Utf8,
523 false,
524 )]));
525
526 let field = Field::new("", make_list_type(struct_type), true);
527 check_basic_random(field).await;
528 }
529
530 #[test_log::test(tokio::test)]
531 async fn test_list_struct_empty() {
532 let fields = Fields::from(vec![Field::new("inner", DataType::UInt64, true)]);
533 let items = UInt64Array::from(Vec::<u64>::new());
534 let structs = StructArray::new(fields, vec![Arc::new(items)], None);
535 let offsets = OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0; 2 * 1024 * 1024 + 1]));
536 let lists = ListArray::new(
537 Arc::new(Field::new("item", structs.data_type().clone(), true)),
538 offsets,
539 Arc::new(structs),
540 None,
541 );
542
543 check_round_trip_encoding_of_data(
544 vec![Arc::new(lists)],
545 &TestCases::default(),
546 HashMap::new(),
547 )
548 .await;
549 }
550
551 #[rstest]
552 #[test_log::test(tokio::test)]
553 async fn test_simple_list(
554 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
555 structural_encoding: &str,
556 ) {
557 let items_builder = Int32Builder::new();
558 let mut list_builder = ListBuilder::new(items_builder);
559 list_builder.append_value([Some(1), Some(2), Some(3)]);
560 list_builder.append_value([Some(4), Some(5)]);
561 list_builder.append_null();
562 list_builder.append_value([Some(6), Some(7), Some(8)]);
563 let list_array = list_builder.finish();
564
565 let mut field_metadata = HashMap::new();
566 field_metadata.insert(
567 STRUCTURAL_ENCODING_META_KEY.to_string(),
568 structural_encoding.into(),
569 );
570
571 let test_cases = TestCases::default()
572 .with_range(0..2)
573 .with_range(0..3)
574 .with_range(1..3)
575 .with_indices(vec![1, 3])
576 .with_indices(vec![2]);
577 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
578 .await;
579 }
580
581 #[rstest]
582 #[test_log::test(tokio::test)]
583 async fn test_simple_nested_list_ends_with_null(
584 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
585 structural_encoding: &str,
586 ) {
587 use arrow_array::Int32Array;
588
589 let values = Int32Array::from(vec![1, 2, 3, 4, 5]);
590 let inner_offsets = ScalarBuffer::<i32>::from(vec![0, 1, 2, 3, 4, 5, 5]);
591 let inner_validity = BooleanBuffer::from(vec![true, true, true, true, true, false]);
592 let outer_offsets = ScalarBuffer::<i32>::from(vec![0, 1, 2, 3, 4, 5, 6, 6]);
593 let outer_validity = BooleanBuffer::from(vec![true, true, true, true, true, true, false]);
594
595 let inner_list = ListArray::new(
596 Arc::new(Field::new("item", DataType::Int32, true)),
597 OffsetBuffer::new(inner_offsets),
598 Arc::new(values),
599 Some(NullBuffer::new(inner_validity)),
600 );
601 let outer_list = ListArray::new(
602 Arc::new(Field::new(
603 "item",
604 DataType::List(Arc::new(Field::new("item", DataType::Int32, true))),
605 true,
606 )),
607 OffsetBuffer::new(outer_offsets),
608 Arc::new(inner_list),
609 Some(NullBuffer::new(outer_validity)),
610 );
611
612 let mut field_metadata = HashMap::new();
613 field_metadata.insert(
614 STRUCTURAL_ENCODING_META_KEY.to_string(),
615 structural_encoding.into(),
616 );
617
618 let test_cases = TestCases::default()
619 .with_range(0..2)
620 .with_range(0..3)
621 .with_range(5..7)
622 .with_indices(vec![1, 6])
623 .with_indices(vec![6])
624 .with_structural_encodings();
625 check_round_trip_encoding_of_data(vec![Arc::new(outer_list)], &test_cases, field_metadata)
626 .await;
627 }
628
629 #[rstest]
630 #[test_log::test(tokio::test)]
631 async fn test_simple_string_list(
632 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
633 structural_encoding: &str,
634 ) {
635 let items_builder = StringBuilder::new();
636 let mut list_builder = ListBuilder::new(items_builder);
637 list_builder.append_value([Some("a"), Some("bc"), Some("def")]);
638 list_builder.append_value([Some("gh"), None]);
639 list_builder.append_null();
640 list_builder.append_value([Some("ijk"), Some("lmnop"), Some("qrs")]);
641 let list_array = list_builder.finish();
642
643 let mut field_metadata = HashMap::new();
644 field_metadata.insert(
645 STRUCTURAL_ENCODING_META_KEY.to_string(),
646 structural_encoding.into(),
647 );
648
649 let test_cases = TestCases::default()
650 .with_range(0..2)
651 .with_range(0..3)
652 .with_range(1..3)
653 .with_indices(vec![1, 3])
654 .with_indices(vec![2])
655 .with_structural_encodings();
656 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
657 .await;
658 }
659
660 #[rstest]
661 #[test_log::test(tokio::test)]
662 async fn test_simple_string_list_no_null(
663 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
664 structural_encoding: &str,
665 ) {
666 let items_builder = StringBuilder::new();
667 let mut list_builder = ListBuilder::new(items_builder);
668 list_builder.append_value([Some("a"), Some("bc"), Some("def")]);
669 list_builder.append_value([Some("gh"), Some("zxy")]);
670 list_builder.append_value([Some("gh"), Some("z")]);
671 list_builder.append_value([Some("ijk"), Some("lmnop"), Some("qrs")]);
672 let list_array = list_builder.finish();
673
674 let mut field_metadata = HashMap::new();
675 field_metadata.insert(
676 STRUCTURAL_ENCODING_META_KEY.to_string(),
677 structural_encoding.into(),
678 );
679
680 let test_cases = TestCases::default()
681 .with_range(0..2)
682 .with_range(0..3)
683 .with_range(1..3)
684 .with_indices(vec![1, 3])
685 .with_indices(vec![2])
686 .with_structural_encodings();
687 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
688 .await;
689 }
690
691 #[rstest]
692 #[test_log::test(tokio::test)]
693 async fn test_simple_sliced_list(
694 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
695 structural_encoding: &str,
696 ) {
697 let items_builder = Int32Builder::new();
698 let mut list_builder = ListBuilder::new(items_builder);
699 list_builder.append_value([Some(1), Some(2), Some(3)]);
700 list_builder.append_value([Some(4), Some(5)]);
701 list_builder.append_null();
702 list_builder.append_value([Some(6), Some(7), Some(8)]);
703 let list_array = list_builder.finish();
704
705 let list_array = list_array.slice(1, 2);
706
707 let mut field_metadata = HashMap::new();
708 field_metadata.insert(
709 STRUCTURAL_ENCODING_META_KEY.to_string(),
710 structural_encoding.into(),
711 );
712
713 let test_cases = TestCases::default()
714 .with_range(0..2)
715 .with_range(1..2)
716 .with_indices(vec![0])
717 .with_indices(vec![1])
718 .with_structural_encodings();
719 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
720 .await;
721 }
722
723 #[test_log::test(tokio::test)]
724 async fn test_simple_list_dict() {
725 let values = LargeStringArray::from_iter_values(["a", "bb", "ccc"]);
726 let indices = UInt8Array::from(vec![0, 1, 2, 0, 1, 2, 0, 1, 2]);
727 let dict_array = DictionaryArray::new(indices, Arc::new(values));
728 let offsets = OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, 3, 5, 6, 9]));
729 let list_array = ListArray::new(
730 Arc::new(Field::new("item", dict_array.data_type().clone(), true)),
731 offsets,
732 Arc::new(dict_array),
733 None,
734 );
735
736 let test_cases = TestCases::default()
737 .with_range(0..2)
738 .with_range(1..3)
739 .with_range(2..4)
740 .with_indices(vec![1])
741 .with_indices(vec![2]);
742 check_round_trip_encoding_of_data(
743 vec![Arc::new(list_array)],
744 &test_cases,
745 HashMap::default(),
746 )
747 .await;
748 }
749
750 #[test_log::test(tokio::test)]
751 async fn test_simple_list_all_null() {
752 let items = UInt64Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
753 let offsets = ScalarBuffer::<i32>::from(vec![0, 5, 8, 10]);
754 let offsets = OffsetBuffer::new(offsets);
755 let list_validity = NullBuffer::new(BooleanBuffer::from(vec![false, false, false]));
756
757 let list_arr = ListArray::new(
759 Arc::new(Field::new("item", DataType::UInt64, false)),
760 offsets,
761 Arc::new(items),
762 Some(list_validity),
763 );
764
765 let test_cases = TestCases::default()
766 .with_range(0..3)
767 .with_range(1..2)
768 .with_indices(vec![1])
769 .with_indices(vec![2])
770 .with_structural_encodings();
771 check_round_trip_encoding_of_data(
772 vec![Arc::new(list_arr)],
773 &test_cases,
774 HashMap::default(),
775 )
776 .await;
777 }
778
779 #[rstest]
780 #[test_log::test(tokio::test)]
781 async fn test_list_with_garbage_nulls(
782 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
783 structural_encoding: &str,
784 ) {
785 let items = UInt64Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
788 let offsets = ScalarBuffer::<i32>::from(vec![0, 5, 8, 10]);
789 let offsets = OffsetBuffer::new(offsets);
790 let list_validity = NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
791 let list_arr = ListArray::new(
792 Arc::new(Field::new("item", DataType::UInt64, true)),
793 offsets,
794 Arc::new(items),
795 Some(list_validity),
796 );
797
798 let mut field_metadata = HashMap::new();
799 field_metadata.insert(
800 STRUCTURAL_ENCODING_META_KEY.to_string(),
801 structural_encoding.into(),
802 );
803
804 let test_cases = TestCases::default()
805 .with_range(0..3)
806 .with_range(1..2)
807 .with_indices(vec![1])
808 .with_indices(vec![2])
809 .with_structural_encodings();
810 check_round_trip_encoding_of_data(vec![Arc::new(list_arr)], &test_cases, field_metadata)
811 .await;
812 }
813
814 #[rstest]
815 #[test_log::test(tokio::test)]
816 async fn test_simple_two_page_list(
817 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
818 structural_encoding: &str,
819 ) {
820 let items_builder = Int64Builder::new();
824 let mut list_builder = ListBuilder::new(items_builder);
825 for i in 0..512 {
826 list_builder.append_value([Some(i), Some(i * 2)]);
827 }
828 let list_array_1 = list_builder.finish();
829
830 let items_builder = Int64Builder::new();
831 let mut list_builder = ListBuilder::new(items_builder);
832 for i in 0..512 {
833 let i = i + 512;
834 list_builder.append_value([Some(i), Some(i * 2)]);
835 }
836 let list_array_2 = list_builder.finish();
837
838 let mut metadata = HashMap::new();
839 metadata.insert(
840 STRUCTURAL_ENCODING_META_KEY.to_string(),
841 structural_encoding.into(),
842 );
843
844 let test_cases = TestCases::default()
845 .with_structural_encodings()
846 .with_page_sizes(vec![100])
847 .with_range(800..900);
848 check_round_trip_encoding_of_data(
849 vec![Arc::new(list_array_1), Arc::new(list_array_2)],
850 &test_cases,
851 metadata,
852 )
853 .await;
854 }
855
856 #[test_log::test(tokio::test)]
857 async fn test_simple_large_list() {
858 let items_builder = Int32Builder::new();
859 let mut list_builder = LargeListBuilder::new(items_builder);
860 list_builder.append_value([Some(1), Some(2), Some(3)]);
861 list_builder.append_value([Some(4), Some(5)]);
862 list_builder.append_null();
863 list_builder.append_value([Some(6), Some(7), Some(8)]);
864 let list_array = list_builder.finish();
865
866 let test_cases = TestCases::default()
867 .with_range(0..2)
868 .with_range(0..3)
869 .with_range(1..3)
870 .with_indices(vec![1, 3]);
871 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, HashMap::new())
872 .await;
873 }
874
875 #[rstest]
876 #[test_log::test(tokio::test)]
877 async fn test_empty_lists(
878 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
879 structural_encoding: &str,
880 ) {
881 let mut field_metadata = HashMap::new();
882 field_metadata.insert(
883 STRUCTURAL_ENCODING_META_KEY.to_string(),
884 structural_encoding.into(),
885 );
886
887 let values = [vec![Some(1), Some(2), Some(3)], vec![], vec![None]];
890 for order in [[0, 1, 2], [1, 0, 2], [2, 0, 1]] {
892 let items_builder = Int32Builder::new();
893 let mut list_builder = ListBuilder::new(items_builder);
894 for idx in order {
895 list_builder.append_value(values[idx].clone());
896 }
897 let list_array = Arc::new(list_builder.finish());
898 let test_cases = TestCases::default()
899 .with_indices(vec![1])
900 .with_indices(vec![0])
901 .with_indices(vec![2])
902 .with_indices(vec![0, 1]);
903 check_round_trip_encoding_of_data(
904 vec![list_array.clone()],
905 &test_cases,
906 field_metadata.clone(),
907 )
908 .await;
909 let test_cases = test_cases.with_batch_size(1);
910 check_round_trip_encoding_of_data(
911 vec![list_array],
912 &test_cases,
913 field_metadata.clone(),
914 )
915 .await;
916 }
917
918 let items_builder = Int32Builder::new();
923 let mut list_builder = ListBuilder::new(items_builder);
924 list_builder.append(true);
925 list_builder.append_null();
926 list_builder.append(true);
927 let list_array = Arc::new(list_builder.finish());
928
929 let test_cases = TestCases::default().with_range(0..2).with_indices(vec![1]);
930 check_round_trip_encoding_of_data(
931 vec![list_array.clone()],
932 &test_cases,
933 field_metadata.clone(),
934 )
935 .await;
936 let test_cases = test_cases.with_batch_size(1);
937 check_round_trip_encoding_of_data(vec![list_array], &test_cases, field_metadata.clone())
938 .await;
939
940 let items_builder = StringBuilder::new();
945 let mut list_builder = ListBuilder::new(items_builder);
946 list_builder.append(true);
947 list_builder.append_null();
948 list_builder.append(true);
949 let list_array = Arc::new(list_builder.finish());
950
951 let test_cases = TestCases::default().with_range(0..2).with_indices(vec![1]);
952 check_round_trip_encoding_of_data(
953 vec![list_array.clone()],
954 &test_cases,
955 field_metadata.clone(),
956 )
957 .await;
958 let test_cases = test_cases.with_batch_size(1);
959 check_round_trip_encoding_of_data(vec![list_array], &test_cases, field_metadata.clone())
960 .await;
961
962 let items_builder = Int32Builder::new();
965 let mut list_builder = ListBuilder::new(items_builder);
966 list_builder.append_null();
967 list_builder.append_null();
968 list_builder.append_null();
969 let list_array = Arc::new(list_builder.finish());
970
971 let test_cases = TestCases::default().with_range(0..2).with_indices(vec![1]);
972 check_round_trip_encoding_of_data(
973 vec![list_array.clone()],
974 &test_cases,
975 field_metadata.clone(),
976 )
977 .await;
978 let test_cases = test_cases.with_batch_size(1);
979 check_round_trip_encoding_of_data(vec![list_array], &test_cases, field_metadata.clone())
980 .await;
981
982 let items_builder = Int32Builder::new();
985 let mut list_builder = ListBuilder::new(items_builder);
986 list_builder.append_null();
987 list_builder.append_null();
988 list_builder.append_null();
989 let list_array = Arc::new(list_builder.finish());
990
991 let struct_validity = NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
992 let struct_array = Arc::new(StructArray::new(
993 Fields::from(vec![Field::new(
994 "lists",
995 list_array.data_type().clone(),
996 true,
997 )]),
998 vec![list_array],
999 Some(struct_validity),
1000 ));
1001
1002 let test_cases = TestCases::default()
1003 .with_range(0..2)
1004 .with_indices(vec![1])
1005 .with_structural_encodings();
1006 check_round_trip_encoding_of_data(
1007 vec![struct_array.clone()],
1008 &test_cases,
1009 field_metadata.clone(),
1010 )
1011 .await;
1012 let test_cases = test_cases.with_batch_size(1);
1013 check_round_trip_encoding_of_data(vec![struct_array], &test_cases, field_metadata.clone())
1014 .await;
1015 }
1016
1017 #[test_log::test(tokio::test)]
1018 async fn test_empty_list_list() {
1019 let items_builder = Int32Builder::new();
1020 let list_builder = ListBuilder::new(items_builder);
1021 let mut outer_list_builder = ListBuilder::new(list_builder);
1022 outer_list_builder.append_null();
1023 outer_list_builder.append_null();
1024 outer_list_builder.append_null();
1025 let list_array = Arc::new(outer_list_builder.finish());
1026
1027 let test_cases = TestCases::default().with_structural_encodings();
1028 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1029 }
1030
1031 #[test_log::test(tokio::test)]
1032 #[ignore] async fn test_jumbo_list() {
1034 let items = BooleanArray::new_null(1024 * 1024);
1038 let offsets = OffsetBuffer::new(ScalarBuffer::from(vec![0, 1024 * 1024]));
1039 let list_arr = Arc::new(ListArray::new(
1040 Arc::new(Field::new("item", DataType::Boolean, true)),
1041 offsets,
1042 Arc::new(items),
1043 None,
1044 )) as ArrayRef;
1045 let arrs = vec![list_arr; 5000];
1046
1047 let test_cases = TestCases::default().without_validation();
1049 check_round_trip_encoding_of_data(arrs, &test_cases, HashMap::new()).await;
1050 }
1051
1052 #[tokio::test]
1056 async fn test_fuzz_issue_4466() {
1057 let list_sizes = vec![
1060 13, 18, 12, 7, 14, 12, 6, 13, 18, 8, 6, 11, 17, 12, 8, 19, 5, 6, 10, 13, 8, 6, 10, 4, 8, 16, 14, 12, 18, 9, 17, 8, 14, 18, 15, 3, 2, 4, 5, 1, 3, 13, 1, 2, 10, 4, 10, 18, 7, 14, 18, 13, 9, 17, 3, 13, 10, 14, 8, 19, 17, 10, 5, 11, 6, 15, 10, 18, 18, 20, 16, 11, 12, 15, 7, 9, 3, 10, 20, 5, 2, 3, 17, 4, 8, 12, 15, 6, 3, 20, 15, 20, 1, 19, 16, ];
1071
1072 let mut list_builder = ListBuilder::new(Int32Builder::new());
1074 let mut total_values = 0;
1075
1076 for size in &list_sizes {
1077 for i in 0..*size {
1078 list_builder.values().append_value(i);
1079 }
1080 list_builder.append(true);
1081 total_values += size;
1082 }
1083
1084 let list_array = Arc::new(list_builder.finish());
1085
1086 assert_eq!(list_array.len(), 95);
1088 assert_eq!(total_values, 1025);
1089
1090 let test_cases = TestCases::default().with_structural_encodings();
1093
1094 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1098 }
1099
1100 #[rstest]
1101 #[test_log::test(tokio::test)]
1102 async fn test_sparse_large_string_list(
1103 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
1104 structural_encoding: &str,
1105 ) {
1106 let levels_per_chunk =
1109 crate::encodings::logical::primitive::miniblock::max_repdef_levels_per_chunk(2);
1110 let num_rows = (levels_per_chunk * 3) as u32;
1111 let num_non_empty = 100u32;
1112 let strings_per_list = 10;
1113
1114 let items_builder = StringBuilder::new();
1115 let mut list_builder = ListBuilder::new(items_builder);
1116
1117 let step = num_rows / num_non_empty;
1119 let mut next_non_empty = step / 2;
1120
1121 for i in 0..num_rows {
1122 if i == next_non_empty {
1123 let vals: Vec<Option<&str>> = (0..strings_per_list)
1124 .map(|j| match j % 4 {
1125 0 => Some("a"),
1126 1 => Some("bb"),
1127 2 => Some("ccc"),
1128 _ => Some("d"),
1129 })
1130 .collect();
1131 list_builder.append_value(vals);
1132 next_non_empty = next_non_empty.saturating_add(step);
1133 } else {
1134 list_builder.append_value([] as [Option<&str>; 0]);
1135 }
1136 }
1137 let list_array = list_builder.finish();
1138
1139 let mut field_metadata = HashMap::new();
1140 field_metadata.insert(
1141 STRUCTURAL_ENCODING_META_KEY.to_string(),
1142 structural_encoding.into(),
1143 );
1144
1145 let test_cases = TestCases::default()
1146 .with_range(0..1000)
1147 .with_range(0..num_rows as u64)
1148 .with_indices(vec![0, (step / 2) as u64, num_rows as u64 - 1])
1149 .with_dense_encodings();
1150 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
1151 .await;
1152 }
1153
1154 #[test_log::test(tokio::test)]
1155 async fn test_sparse_boolean_list_uses_miniblock() {
1156 let num_rows = 200_000usize;
1160 let num_non_empty = 10usize;
1161 let booleans_per_list = 8usize;
1162 let step = num_rows / num_non_empty;
1163
1164 let mut offsets = Vec::with_capacity(num_rows + 1);
1165 let mut values = Vec::with_capacity(num_non_empty * booleans_per_list);
1166 offsets.push(0i32);
1167
1168 let mut next_non_empty = step / 2;
1169 for row in 0..num_rows {
1170 if row == next_non_empty {
1171 values.extend((0..booleans_per_list).map(|idx| idx % 2 == 0));
1172 next_non_empty += step;
1173 }
1174 offsets.push(values.len() as i32);
1175 }
1176
1177 let items = BooleanArray::from(values);
1178 let list_array = ListArray::new(
1179 Arc::new(Field::new("item", DataType::Boolean, true)),
1180 OffsetBuffer::new(ScalarBuffer::from(offsets)),
1181 Arc::new(items),
1182 None,
1183 );
1184
1185 let test_cases = TestCases::default()
1186 .with_range(0..1000)
1187 .with_range(0..num_rows as u64)
1188 .with_indices(vec![0, (step / 2) as u64, num_rows as u64 - 1])
1189 .with_dense_encodings();
1190 let list_array = Arc::new(list_array) as ArrayRef;
1191 let pages = encode_v22_pages(list_array.clone()).await;
1192 assert_split_miniblock_layout(&pages, false);
1193 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1194 }
1195
1196 #[test_log::test(tokio::test)]
1197 async fn test_sparse_boolean_list_with_long_empty_prefix() {
1198 let empty_prefix_rows = 70_000usize;
1199 let trailing_empty_rows = 9usize;
1200 let booleans_per_list = 8usize;
1201 let num_rows = empty_prefix_rows + 1 + trailing_empty_rows;
1202
1203 let mut offsets = Vec::with_capacity(num_rows + 1);
1204 offsets.extend(std::iter::repeat_n(0i32, empty_prefix_rows + 1));
1205 let values = (0..booleans_per_list)
1206 .map(|idx| idx % 2 == 0)
1207 .collect::<Vec<_>>();
1208 offsets.push(values.len() as i32);
1209 offsets.extend(std::iter::repeat_n(
1210 values.len() as i32,
1211 trailing_empty_rows,
1212 ));
1213
1214 let items = BooleanArray::from(values);
1215 let list_array = ListArray::new(
1216 Arc::new(Field::new("item", DataType::Boolean, true)),
1217 OffsetBuffer::new(ScalarBuffer::from(offsets)),
1218 Arc::new(items),
1219 None,
1220 );
1221
1222 let test_cases = TestCases::default()
1223 .with_range(0..num_rows as u64)
1224 .with_indices(vec![0, empty_prefix_rows as u64, num_rows as u64 - 1])
1225 .with_dense_encodings();
1226 let list_array = Arc::new(list_array) as ArrayRef;
1227 let pages = encode_v22_pages(list_array.clone()).await;
1228 assert_split_miniblock_layout(&pages, true);
1229 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1230 }
1231
1232 #[test_log::test(tokio::test)]
1233 async fn test_sparse_boolean_list_with_long_null_prefix() {
1234 let null_prefix_rows = 70_000usize;
1235 let trailing_empty_rows = 9usize;
1236 let booleans_per_list = 8usize;
1237 let num_rows = null_prefix_rows + 1 + trailing_empty_rows;
1238
1239 let mut offsets = Vec::with_capacity(num_rows + 1);
1240 offsets.extend(std::iter::repeat_n(0i32, null_prefix_rows + 1));
1241 let values = (0..booleans_per_list)
1242 .map(|idx| idx % 2 == 0)
1243 .collect::<Vec<_>>();
1244 offsets.push(values.len() as i32);
1245 offsets.extend(std::iter::repeat_n(
1246 values.len() as i32,
1247 trailing_empty_rows,
1248 ));
1249 let validity = BooleanBuffer::from_iter((0..num_rows).map(|row| row >= null_prefix_rows));
1250
1251 let items = BooleanArray::from(values);
1252 let list_array = ListArray::new(
1253 Arc::new(Field::new("item", DataType::Boolean, true)),
1254 OffsetBuffer::new(ScalarBuffer::from(offsets)),
1255 Arc::new(items),
1256 Some(NullBuffer::new(validity)),
1257 );
1258
1259 let test_cases = TestCases::default()
1260 .with_range(0..num_rows as u64)
1261 .with_indices(vec![0, null_prefix_rows as u64, num_rows as u64 - 1])
1262 .with_dense_encodings();
1263 let list_array = Arc::new(list_array) as ArrayRef;
1264 let pages = encode_v22_pages(list_array.clone()).await;
1265 assert_split_miniblock_layout(&pages, true);
1266 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1267 }
1268
1269 #[test_log::test(tokio::test)]
1270 async fn test_sparse_boolean_list_with_amortized_long_empty_prefix() {
1271 let empty_prefix_rows = 62_000usize;
1272 let booleans_per_list = 8_192usize;
1273 let num_rows = empty_prefix_rows + 1;
1274
1275 let mut offsets = Vec::with_capacity(num_rows + 1);
1276 offsets.extend(std::iter::repeat_n(0i32, empty_prefix_rows + 1));
1277 let values = (0..booleans_per_list)
1278 .map(|idx| idx % 2 == 0)
1279 .collect::<Vec<_>>();
1280 offsets.push(values.len() as i32);
1281
1282 let items = BooleanArray::from(values);
1283 let list_array = ListArray::new(
1284 Arc::new(Field::new("item", DataType::Boolean, true)),
1285 OffsetBuffer::new(ScalarBuffer::from(offsets)),
1286 Arc::new(items),
1287 None,
1288 );
1289
1290 let test_cases = TestCases::default()
1291 .with_range(0..num_rows as u64)
1292 .with_indices(vec![0, empty_prefix_rows as u64])
1293 .with_dense_encodings();
1294 let list_array = Arc::new(list_array) as ArrayRef;
1295 let pages = encode_v22_pages(list_array.clone()).await;
1296 assert_split_miniblock_layout(&pages, true);
1297 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1298 }
1299
1300 #[test_log::test(tokio::test)]
1301 async fn test_nested_sparse_boolean_list_fails_without_panic() {
1302 let empty_inner_lists = 70_000usize;
1303 let booleans_per_list = 8usize;
1304
1305 let mut inner_offsets = vec![0i32; empty_inner_lists + 1];
1306 let values = (0..booleans_per_list)
1307 .map(|idx| idx % 2 == 0)
1308 .collect::<Vec<_>>();
1309 inner_offsets.push(values.len() as i32);
1310
1311 let inner_items = BooleanArray::from(values);
1312 let inner_list = ListArray::new(
1313 Arc::new(Field::new("item", DataType::Boolean, true)),
1314 OffsetBuffer::new(ScalarBuffer::from(inner_offsets)),
1315 Arc::new(inner_items),
1316 None,
1317 );
1318 let outer_list = ListArray::new(
1319 Arc::new(Field::new("item", inner_list.data_type().clone(), true)),
1320 OffsetBuffer::new(ScalarBuffer::from(vec![0i32, empty_inner_lists as i32 + 1])),
1321 Arc::new(inner_list),
1322 None,
1323 );
1324
1325 let err = try_encode_v22_pages(Arc::new(outer_list))
1326 .await
1327 .unwrap_err();
1328 assert!(
1329 err.to_string().contains("Mini-block cannot encode"),
1330 "unexpected error: {err}"
1331 );
1332 }
1333
1334 #[test_log::test(tokio::test)]
1335 async fn test_nested_sparse_string_single_row_falls_back_to_fullzip() {
1336 let empty_inner_lists = 70_000usize;
1337
1338 let mut inner_offsets = vec![0i32; empty_inner_lists + 1];
1339 inner_offsets.push(1);
1340 inner_offsets.push(2);
1341
1342 let mut strings = StringBuilder::new();
1343 strings.append_value("value");
1344 strings.append_value("other");
1345 let inner_items = strings.finish();
1346 let inner_list = ListArray::new(
1347 Arc::new(Field::new("item", DataType::Utf8, true)),
1348 OffsetBuffer::new(ScalarBuffer::from(inner_offsets)),
1349 Arc::new(inner_items),
1350 None,
1351 );
1352 let outer_list = ListArray::new(
1353 Arc::new(Field::new("item", inner_list.data_type().clone(), true)),
1354 OffsetBuffer::new(ScalarBuffer::from(vec![0i32, empty_inner_lists as i32 + 2])),
1355 Arc::new(inner_list),
1356 None,
1357 );
1358
1359 let outer_list = Arc::new(outer_list) as ArrayRef;
1360 let pages = encode_v22_pages(outer_list.clone()).await;
1361 assert_has_fullzip_layout(&pages);
1362
1363 let test_cases = TestCases::default()
1364 .with_range(0..1)
1365 .with_indices(vec![0])
1366 .with_dense_encodings();
1367 check_round_trip_encoding_of_data(vec![outer_list], &test_cases, HashMap::new()).await;
1368 }
1369
1370 fn make_hnsw_shaped_list_u32() -> ListArray {
1375 const DENSE_ROWS: u32 = 40_000;
1376 const NEIGHBORS_PER_ROW: u32 = 32;
1377 const EMPTY_TAIL_ROWS: u32 = 240_000;
1378
1379 let mut list_builder = ListBuilder::new(UInt32Builder::new());
1380 let mut next_val: u32 = 0;
1381 for _ in 0..DENSE_ROWS {
1382 for _ in 0..NEIGHBORS_PER_ROW {
1383 list_builder.values().append_value(next_val);
1384 next_val = next_val.wrapping_add(1);
1385 }
1386 list_builder.append(true);
1387 }
1388 for _ in 0..EMPTY_TAIL_ROWS {
1389 list_builder.append(true);
1390 }
1391 list_builder.finish()
1392 }
1393
1394 #[test_log::test(tokio::test)]
1404 async fn test_list_hnsw_shape_splits_to_miniblock_v2_2() {
1405 let list_array = make_hnsw_shaped_list_u32();
1406 let dense_rows: u64 = 40_000;
1407 let total_rows = list_array.len() as u64;
1408
1409 let test_cases = TestCases::default()
1410 .with_range(0..1000)
1411 .with_range(dense_rows.saturating_sub(8)..(dense_rows + 8))
1412 .with_range(0..total_rows)
1413 .with_indices(vec![0, dense_rows - 1, dense_rows, total_rows - 1])
1414 .with_encoding(TestEncoding::StructuralU32);
1415 let list_array = Arc::new(list_array) as ArrayRef;
1416 let pages = encode_v22_pages(list_array.clone()).await;
1417 assert_split_miniblock_layout(&pages, true);
1418 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1419 }
1420
1421 #[test_log::test(tokio::test)]
1427 async fn test_forced_miniblock_hnsw_shape_splits_to_miniblock_v2_2() {
1428 let list_array = make_hnsw_shaped_list_u32();
1429 let total_rows = list_array.len() as u64;
1430
1431 let mut field_metadata = HashMap::new();
1432 field_metadata.insert(
1433 STRUCTURAL_ENCODING_META_KEY.to_string(),
1434 STRUCTURAL_ENCODING_MINIBLOCK.into(),
1435 );
1436
1437 let test_cases = TestCases::default()
1438 .with_range(0..total_rows)
1439 .with_encoding(TestEncoding::StructuralU32);
1440 let list_array = Arc::new(list_array) as ArrayRef;
1441 let pages = try_encode_v22_pages_with_metadata(list_array.clone(), field_metadata.clone())
1442 .await
1443 .unwrap();
1444 assert_split_miniblock_layout(&pages, true);
1445 check_round_trip_encoding_of_data(vec![list_array], &test_cases, field_metadata).await;
1446 }
1447}