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 num_rows = 2_500_000u32;
1108 let num_non_empty = 100u32;
1109 let strings_per_list = 10;
1110
1111 let items_builder = StringBuilder::new();
1112 let mut list_builder = ListBuilder::new(items_builder);
1113
1114 let step = num_rows / num_non_empty;
1116 let mut next_non_empty = step / 2;
1117
1118 for i in 0..num_rows {
1119 if i == next_non_empty {
1120 let vals: Vec<Option<&str>> = (0..strings_per_list)
1121 .map(|j| match j % 4 {
1122 0 => Some("a"),
1123 1 => Some("bb"),
1124 2 => Some("ccc"),
1125 _ => Some("d"),
1126 })
1127 .collect();
1128 list_builder.append_value(vals);
1129 next_non_empty = next_non_empty.saturating_add(step);
1130 } else {
1131 list_builder.append_value([] as [Option<&str>; 0]);
1132 }
1133 }
1134 let list_array = list_builder.finish();
1135
1136 let mut field_metadata = HashMap::new();
1137 field_metadata.insert(
1138 STRUCTURAL_ENCODING_META_KEY.to_string(),
1139 structural_encoding.into(),
1140 );
1141
1142 let test_cases = TestCases::default()
1143 .with_range(0..1000)
1144 .with_range(0..num_rows as u64)
1145 .with_indices(vec![0, (step / 2) as u64, num_rows as u64 - 1])
1146 .with_dense_encodings();
1147 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
1148 .await;
1149 }
1150
1151 #[test_log::test(tokio::test)]
1152 async fn test_sparse_boolean_list_uses_miniblock() {
1153 let num_rows = 200_000usize;
1157 let num_non_empty = 10usize;
1158 let booleans_per_list = 8usize;
1159 let step = num_rows / num_non_empty;
1160
1161 let mut offsets = Vec::with_capacity(num_rows + 1);
1162 let mut values = Vec::with_capacity(num_non_empty * booleans_per_list);
1163 offsets.push(0i32);
1164
1165 let mut next_non_empty = step / 2;
1166 for row in 0..num_rows {
1167 if row == next_non_empty {
1168 values.extend((0..booleans_per_list).map(|idx| idx % 2 == 0));
1169 next_non_empty += step;
1170 }
1171 offsets.push(values.len() as i32);
1172 }
1173
1174 let items = BooleanArray::from(values);
1175 let list_array = ListArray::new(
1176 Arc::new(Field::new("item", DataType::Boolean, true)),
1177 OffsetBuffer::new(ScalarBuffer::from(offsets)),
1178 Arc::new(items),
1179 None,
1180 );
1181
1182 let test_cases = TestCases::default()
1183 .with_range(0..1000)
1184 .with_range(0..num_rows as u64)
1185 .with_indices(vec![0, (step / 2) as u64, num_rows as u64 - 1])
1186 .with_dense_encodings();
1187 let list_array = Arc::new(list_array) as ArrayRef;
1188 let pages = encode_v22_pages(list_array.clone()).await;
1189 assert_split_miniblock_layout(&pages, false);
1190 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1191 }
1192
1193 #[test_log::test(tokio::test)]
1194 async fn test_sparse_boolean_list_with_long_empty_prefix() {
1195 let empty_prefix_rows = 70_000usize;
1196 let trailing_empty_rows = 9usize;
1197 let booleans_per_list = 8usize;
1198 let num_rows = empty_prefix_rows + 1 + trailing_empty_rows;
1199
1200 let mut offsets = Vec::with_capacity(num_rows + 1);
1201 offsets.extend(std::iter::repeat_n(0i32, empty_prefix_rows + 1));
1202 let values = (0..booleans_per_list)
1203 .map(|idx| idx % 2 == 0)
1204 .collect::<Vec<_>>();
1205 offsets.push(values.len() as i32);
1206 offsets.extend(std::iter::repeat_n(
1207 values.len() as i32,
1208 trailing_empty_rows,
1209 ));
1210
1211 let items = BooleanArray::from(values);
1212 let list_array = ListArray::new(
1213 Arc::new(Field::new("item", DataType::Boolean, true)),
1214 OffsetBuffer::new(ScalarBuffer::from(offsets)),
1215 Arc::new(items),
1216 None,
1217 );
1218
1219 let test_cases = TestCases::default()
1220 .with_range(0..num_rows as u64)
1221 .with_indices(vec![0, empty_prefix_rows as u64, num_rows as u64 - 1])
1222 .with_dense_encodings();
1223 let list_array = Arc::new(list_array) as ArrayRef;
1224 let pages = encode_v22_pages(list_array.clone()).await;
1225 assert_split_miniblock_layout(&pages, true);
1226 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1227 }
1228
1229 #[test_log::test(tokio::test)]
1230 async fn test_sparse_boolean_list_with_long_null_prefix() {
1231 let null_prefix_rows = 70_000usize;
1232 let trailing_empty_rows = 9usize;
1233 let booleans_per_list = 8usize;
1234 let num_rows = null_prefix_rows + 1 + trailing_empty_rows;
1235
1236 let mut offsets = Vec::with_capacity(num_rows + 1);
1237 offsets.extend(std::iter::repeat_n(0i32, null_prefix_rows + 1));
1238 let values = (0..booleans_per_list)
1239 .map(|idx| idx % 2 == 0)
1240 .collect::<Vec<_>>();
1241 offsets.push(values.len() as i32);
1242 offsets.extend(std::iter::repeat_n(
1243 values.len() as i32,
1244 trailing_empty_rows,
1245 ));
1246 let validity = BooleanBuffer::from_iter((0..num_rows).map(|row| row >= null_prefix_rows));
1247
1248 let items = BooleanArray::from(values);
1249 let list_array = ListArray::new(
1250 Arc::new(Field::new("item", DataType::Boolean, true)),
1251 OffsetBuffer::new(ScalarBuffer::from(offsets)),
1252 Arc::new(items),
1253 Some(NullBuffer::new(validity)),
1254 );
1255
1256 let test_cases = TestCases::default()
1257 .with_range(0..num_rows as u64)
1258 .with_indices(vec![0, null_prefix_rows as u64, num_rows as u64 - 1])
1259 .with_dense_encodings();
1260 let list_array = Arc::new(list_array) as ArrayRef;
1261 let pages = encode_v22_pages(list_array.clone()).await;
1262 assert_split_miniblock_layout(&pages, true);
1263 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1264 }
1265
1266 #[test_log::test(tokio::test)]
1267 async fn test_sparse_boolean_list_with_amortized_long_empty_prefix() {
1268 let empty_prefix_rows = 62_000usize;
1269 let booleans_per_list = 8_192usize;
1270 let num_rows = empty_prefix_rows + 1;
1271
1272 let mut offsets = Vec::with_capacity(num_rows + 1);
1273 offsets.extend(std::iter::repeat_n(0i32, empty_prefix_rows + 1));
1274 let values = (0..booleans_per_list)
1275 .map(|idx| idx % 2 == 0)
1276 .collect::<Vec<_>>();
1277 offsets.push(values.len() as i32);
1278
1279 let items = BooleanArray::from(values);
1280 let list_array = ListArray::new(
1281 Arc::new(Field::new("item", DataType::Boolean, true)),
1282 OffsetBuffer::new(ScalarBuffer::from(offsets)),
1283 Arc::new(items),
1284 None,
1285 );
1286
1287 let test_cases = TestCases::default()
1288 .with_range(0..num_rows as u64)
1289 .with_indices(vec![0, empty_prefix_rows as u64])
1290 .with_dense_encodings();
1291 let list_array = Arc::new(list_array) as ArrayRef;
1292 let pages = encode_v22_pages(list_array.clone()).await;
1293 assert_split_miniblock_layout(&pages, true);
1294 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1295 }
1296
1297 #[test_log::test(tokio::test)]
1298 async fn test_nested_sparse_boolean_list_fails_without_panic() {
1299 let empty_inner_lists = 70_000usize;
1300 let booleans_per_list = 8usize;
1301
1302 let mut inner_offsets = vec![0i32; empty_inner_lists + 1];
1303 let values = (0..booleans_per_list)
1304 .map(|idx| idx % 2 == 0)
1305 .collect::<Vec<_>>();
1306 inner_offsets.push(values.len() as i32);
1307
1308 let inner_items = BooleanArray::from(values);
1309 let inner_list = ListArray::new(
1310 Arc::new(Field::new("item", DataType::Boolean, true)),
1311 OffsetBuffer::new(ScalarBuffer::from(inner_offsets)),
1312 Arc::new(inner_items),
1313 None,
1314 );
1315 let outer_list = ListArray::new(
1316 Arc::new(Field::new("item", inner_list.data_type().clone(), true)),
1317 OffsetBuffer::new(ScalarBuffer::from(vec![0i32, empty_inner_lists as i32 + 1])),
1318 Arc::new(inner_list),
1319 None,
1320 );
1321
1322 let err = try_encode_v22_pages(Arc::new(outer_list))
1323 .await
1324 .unwrap_err();
1325 assert!(
1326 err.to_string().contains("Mini-block cannot encode"),
1327 "unexpected error: {err}"
1328 );
1329 }
1330
1331 #[test_log::test(tokio::test)]
1332 async fn test_nested_sparse_string_single_row_falls_back_to_fullzip() {
1333 let empty_inner_lists = 70_000usize;
1334
1335 let mut inner_offsets = vec![0i32; empty_inner_lists + 1];
1336 inner_offsets.push(1);
1337 inner_offsets.push(2);
1338
1339 let mut strings = StringBuilder::new();
1340 strings.append_value("value");
1341 strings.append_value("other");
1342 let inner_items = strings.finish();
1343 let inner_list = ListArray::new(
1344 Arc::new(Field::new("item", DataType::Utf8, true)),
1345 OffsetBuffer::new(ScalarBuffer::from(inner_offsets)),
1346 Arc::new(inner_items),
1347 None,
1348 );
1349 let outer_list = ListArray::new(
1350 Arc::new(Field::new("item", inner_list.data_type().clone(), true)),
1351 OffsetBuffer::new(ScalarBuffer::from(vec![0i32, empty_inner_lists as i32 + 2])),
1352 Arc::new(inner_list),
1353 None,
1354 );
1355
1356 let outer_list = Arc::new(outer_list) as ArrayRef;
1357 let pages = encode_v22_pages(outer_list.clone()).await;
1358 assert_has_fullzip_layout(&pages);
1359
1360 let test_cases = TestCases::default()
1361 .with_range(0..1)
1362 .with_indices(vec![0])
1363 .with_dense_encodings();
1364 check_round_trip_encoding_of_data(vec![outer_list], &test_cases, HashMap::new()).await;
1365 }
1366
1367 fn make_hnsw_shaped_list_u32() -> ListArray {
1372 const DENSE_ROWS: u32 = 40_000;
1373 const NEIGHBORS_PER_ROW: u32 = 32;
1374 const EMPTY_TAIL_ROWS: u32 = 240_000;
1375
1376 let mut list_builder = ListBuilder::new(UInt32Builder::new());
1377 let mut next_val: u32 = 0;
1378 for _ in 0..DENSE_ROWS {
1379 for _ in 0..NEIGHBORS_PER_ROW {
1380 list_builder.values().append_value(next_val);
1381 next_val = next_val.wrapping_add(1);
1382 }
1383 list_builder.append(true);
1384 }
1385 for _ in 0..EMPTY_TAIL_ROWS {
1386 list_builder.append(true);
1387 }
1388 list_builder.finish()
1389 }
1390
1391 #[test_log::test(tokio::test)]
1401 async fn test_list_hnsw_shape_splits_to_miniblock_v2_2() {
1402 let list_array = make_hnsw_shaped_list_u32();
1403 let dense_rows: u64 = 40_000;
1404 let total_rows = list_array.len() as u64;
1405
1406 let test_cases = TestCases::default()
1407 .with_range(0..1000)
1408 .with_range(dense_rows.saturating_sub(8)..(dense_rows + 8))
1409 .with_range(0..total_rows)
1410 .with_indices(vec![0, dense_rows - 1, dense_rows, total_rows - 1])
1411 .with_encoding(TestEncoding::StructuralU32);
1412 let list_array = Arc::new(list_array) as ArrayRef;
1413 let pages = encode_v22_pages(list_array.clone()).await;
1414 assert_split_miniblock_layout(&pages, true);
1415 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
1416 }
1417
1418 #[test_log::test(tokio::test)]
1424 async fn test_forced_miniblock_hnsw_shape_splits_to_miniblock_v2_2() {
1425 let list_array = make_hnsw_shaped_list_u32();
1426 let total_rows = list_array.len() as u64;
1427
1428 let mut field_metadata = HashMap::new();
1429 field_metadata.insert(
1430 STRUCTURAL_ENCODING_META_KEY.to_string(),
1431 STRUCTURAL_ENCODING_MINIBLOCK.into(),
1432 );
1433
1434 let test_cases = TestCases::default()
1435 .with_range(0..total_rows)
1436 .with_encoding(TestEncoding::StructuralU32);
1437 let list_array = Arc::new(list_array) as ArrayRef;
1438 let pages = try_encode_v22_pages_with_metadata(list_array.clone(), field_metadata.clone())
1439 .await
1440 .unwrap();
1441 assert_split_miniblock_layout(&pages, true);
1442 check_round_trip_encoding_of_data(vec![list_array], &test_cases, field_metadata).await;
1443 }
1444}