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::{
255 testing::{TestCases, check_basic_random, check_round_trip_encoding_of_data},
256 version::LanceFileVersion,
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 #[rstest]
268 #[test_log::test(tokio::test)]
269 async fn test_list(
270 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
271 structural_encoding: &str,
272 ) {
273 let mut field_metadata = HashMap::new();
274 field_metadata.insert(
275 STRUCTURAL_ENCODING_META_KEY.to_string(),
276 structural_encoding.into(),
277 );
278 let field =
279 Field::new("", make_list_type(DataType::Int32), true).with_metadata(field_metadata);
280 check_basic_random(field).await;
281 }
282
283 #[rstest]
284 #[test_log::test(tokio::test)]
285 async fn test_deeply_nested_lists(
286 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
287 structural_encoding: &str,
288 ) {
289 let mut field_metadata = HashMap::new();
290 field_metadata.insert(
291 STRUCTURAL_ENCODING_META_KEY.to_string(),
292 structural_encoding.into(),
293 );
294 let field = Field::new("item", DataType::Int32, true).with_metadata(field_metadata);
295 for _ in 0..5 {
296 let field = Field::new("", make_list_type(field.data_type().clone()), true);
297 check_basic_random(field).await;
298 }
299 }
300
301 #[test_log::test(tokio::test)]
302 async fn test_large_list() {
303 let field = Field::new("", make_large_list_type(DataType::Int32), true);
304 check_basic_random(field).await;
305 }
306
307 #[test_log::test(tokio::test)]
308 async fn test_nested_strings() {
309 let field = Field::new("", make_list_type(DataType::Utf8), true);
310 check_basic_random(field).await;
311 }
312
313 #[test_log::test(tokio::test)]
314 async fn test_nested_list() {
315 let field = Field::new("", make_list_type(make_list_type(DataType::Int32)), true);
316 check_basic_random(field).await;
317 }
318
319 #[test_log::test(tokio::test)]
320 async fn test_list_struct_list() {
321 let struct_type = DataType::Struct(Fields::from(vec![Field::new(
322 "inner_str",
323 DataType::Utf8,
324 false,
325 )]));
326
327 let field = Field::new("", make_list_type(struct_type), true);
328 check_basic_random(field).await;
329 }
330
331 #[test_log::test(tokio::test)]
332 async fn test_list_struct_empty() {
333 let fields = Fields::from(vec![Field::new("inner", DataType::UInt64, true)]);
334 let items = UInt64Array::from(Vec::<u64>::new());
335 let structs = StructArray::new(fields, vec![Arc::new(items)], None);
336 let offsets = OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0; 2 * 1024 * 1024 + 1]));
337 let lists = ListArray::new(
338 Arc::new(Field::new("item", structs.data_type().clone(), true)),
339 offsets,
340 Arc::new(structs),
341 None,
342 );
343
344 check_round_trip_encoding_of_data(
345 vec![Arc::new(lists)],
346 &TestCases::default(),
347 HashMap::new(),
348 )
349 .await;
350 }
351
352 #[rstest]
353 #[test_log::test(tokio::test)]
354 async fn test_simple_list(
355 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
356 structural_encoding: &str,
357 ) {
358 let items_builder = Int32Builder::new();
359 let mut list_builder = ListBuilder::new(items_builder);
360 list_builder.append_value([Some(1), Some(2), Some(3)]);
361 list_builder.append_value([Some(4), Some(5)]);
362 list_builder.append_null();
363 list_builder.append_value([Some(6), Some(7), Some(8)]);
364 let list_array = list_builder.finish();
365
366 let mut field_metadata = HashMap::new();
367 field_metadata.insert(
368 STRUCTURAL_ENCODING_META_KEY.to_string(),
369 structural_encoding.into(),
370 );
371
372 let test_cases = TestCases::default()
373 .with_range(0..2)
374 .with_range(0..3)
375 .with_range(1..3)
376 .with_indices(vec![1, 3])
377 .with_indices(vec![2]);
378 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
379 .await;
380 }
381
382 #[rstest]
383 #[test_log::test(tokio::test)]
384 async fn test_simple_nested_list_ends_with_null(
385 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
386 structural_encoding: &str,
387 ) {
388 use arrow_array::Int32Array;
389
390 let values = Int32Array::from(vec![1, 2, 3, 4, 5]);
391 let inner_offsets = ScalarBuffer::<i32>::from(vec![0, 1, 2, 3, 4, 5, 5]);
392 let inner_validity = BooleanBuffer::from(vec![true, true, true, true, true, false]);
393 let outer_offsets = ScalarBuffer::<i32>::from(vec![0, 1, 2, 3, 4, 5, 6, 6]);
394 let outer_validity = BooleanBuffer::from(vec![true, true, true, true, true, true, false]);
395
396 let inner_list = ListArray::new(
397 Arc::new(Field::new("item", DataType::Int32, true)),
398 OffsetBuffer::new(inner_offsets),
399 Arc::new(values),
400 Some(NullBuffer::new(inner_validity)),
401 );
402 let outer_list = ListArray::new(
403 Arc::new(Field::new(
404 "item",
405 DataType::List(Arc::new(Field::new("item", DataType::Int32, true))),
406 true,
407 )),
408 OffsetBuffer::new(outer_offsets),
409 Arc::new(inner_list),
410 Some(NullBuffer::new(outer_validity)),
411 );
412
413 let mut field_metadata = HashMap::new();
414 field_metadata.insert(
415 STRUCTURAL_ENCODING_META_KEY.to_string(),
416 structural_encoding.into(),
417 );
418
419 let test_cases = TestCases::default()
420 .with_range(0..2)
421 .with_range(0..3)
422 .with_range(5..7)
423 .with_indices(vec![1, 6])
424 .with_indices(vec![6])
425 .with_min_file_version(LanceFileVersion::V2_1);
426 check_round_trip_encoding_of_data(vec![Arc::new(outer_list)], &test_cases, field_metadata)
427 .await;
428 }
429
430 #[rstest]
431 #[test_log::test(tokio::test)]
432 async fn test_simple_string_list(
433 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
434 structural_encoding: &str,
435 ) {
436 let items_builder = StringBuilder::new();
437 let mut list_builder = ListBuilder::new(items_builder);
438 list_builder.append_value([Some("a"), Some("bc"), Some("def")]);
439 list_builder.append_value([Some("gh"), None]);
440 list_builder.append_null();
441 list_builder.append_value([Some("ijk"), Some("lmnop"), Some("qrs")]);
442 let list_array = list_builder.finish();
443
444 let mut field_metadata = HashMap::new();
445 field_metadata.insert(
446 STRUCTURAL_ENCODING_META_KEY.to_string(),
447 structural_encoding.into(),
448 );
449
450 let test_cases = TestCases::default()
451 .with_range(0..2)
452 .with_range(0..3)
453 .with_range(1..3)
454 .with_indices(vec![1, 3])
455 .with_indices(vec![2])
456 .with_min_file_version(LanceFileVersion::V2_1);
457 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
458 .await;
459 }
460
461 #[rstest]
462 #[test_log::test(tokio::test)]
463 async fn test_simple_string_list_no_null(
464 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
465 structural_encoding: &str,
466 ) {
467 let items_builder = StringBuilder::new();
468 let mut list_builder = ListBuilder::new(items_builder);
469 list_builder.append_value([Some("a"), Some("bc"), Some("def")]);
470 list_builder.append_value([Some("gh"), Some("zxy")]);
471 list_builder.append_value([Some("gh"), Some("z")]);
472 list_builder.append_value([Some("ijk"), Some("lmnop"), Some("qrs")]);
473 let list_array = list_builder.finish();
474
475 let mut field_metadata = HashMap::new();
476 field_metadata.insert(
477 STRUCTURAL_ENCODING_META_KEY.to_string(),
478 structural_encoding.into(),
479 );
480
481 let test_cases = TestCases::default()
482 .with_range(0..2)
483 .with_range(0..3)
484 .with_range(1..3)
485 .with_indices(vec![1, 3])
486 .with_indices(vec![2])
487 .with_min_file_version(LanceFileVersion::V2_1);
488 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
489 .await;
490 }
491
492 #[rstest]
493 #[test_log::test(tokio::test)]
494 async fn test_simple_sliced_list(
495 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
496 structural_encoding: &str,
497 ) {
498 let items_builder = Int32Builder::new();
499 let mut list_builder = ListBuilder::new(items_builder);
500 list_builder.append_value([Some(1), Some(2), Some(3)]);
501 list_builder.append_value([Some(4), Some(5)]);
502 list_builder.append_null();
503 list_builder.append_value([Some(6), Some(7), Some(8)]);
504 let list_array = list_builder.finish();
505
506 let list_array = list_array.slice(1, 2);
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()
515 .with_range(0..2)
516 .with_range(1..2)
517 .with_indices(vec![0])
518 .with_indices(vec![1])
519 .with_min_file_version(LanceFileVersion::V2_1);
520 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
521 .await;
522 }
523
524 #[test_log::test(tokio::test)]
525 async fn test_simple_list_dict() {
526 let values = LargeStringArray::from_iter_values(["a", "bb", "ccc"]);
527 let indices = UInt8Array::from(vec![0, 1, 2, 0, 1, 2, 0, 1, 2]);
528 let dict_array = DictionaryArray::new(indices, Arc::new(values));
529 let offsets = OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, 3, 5, 6, 9]));
530 let list_array = ListArray::new(
531 Arc::new(Field::new("item", dict_array.data_type().clone(), true)),
532 offsets,
533 Arc::new(dict_array),
534 None,
535 );
536
537 let test_cases = TestCases::default()
538 .with_range(0..2)
539 .with_range(1..3)
540 .with_range(2..4)
541 .with_indices(vec![1])
542 .with_indices(vec![2]);
543 check_round_trip_encoding_of_data(
544 vec![Arc::new(list_array)],
545 &test_cases,
546 HashMap::default(),
547 )
548 .await;
549 }
550
551 #[test_log::test(tokio::test)]
552 async fn test_simple_list_all_null() {
553 let items = UInt64Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
554 let offsets = ScalarBuffer::<i32>::from(vec![0, 5, 8, 10]);
555 let offsets = OffsetBuffer::new(offsets);
556 let list_validity = NullBuffer::new(BooleanBuffer::from(vec![false, false, false]));
557
558 let list_arr = ListArray::new(
560 Arc::new(Field::new("item", DataType::UInt64, false)),
561 offsets,
562 Arc::new(items),
563 Some(list_validity),
564 );
565
566 let test_cases = TestCases::default()
567 .with_range(0..3)
568 .with_range(1..2)
569 .with_indices(vec![1])
570 .with_indices(vec![2])
571 .with_min_file_version(LanceFileVersion::V2_1);
572 check_round_trip_encoding_of_data(
573 vec![Arc::new(list_arr)],
574 &test_cases,
575 HashMap::default(),
576 )
577 .await;
578 }
579
580 #[rstest]
581 #[test_log::test(tokio::test)]
582 async fn test_list_with_garbage_nulls(
583 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
584 structural_encoding: &str,
585 ) {
586 let items = UInt64Array::from(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
589 let offsets = ScalarBuffer::<i32>::from(vec![0, 5, 8, 10]);
590 let offsets = OffsetBuffer::new(offsets);
591 let list_validity = NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
592 let list_arr = ListArray::new(
593 Arc::new(Field::new("item", DataType::UInt64, true)),
594 offsets,
595 Arc::new(items),
596 Some(list_validity),
597 );
598
599 let mut field_metadata = HashMap::new();
600 field_metadata.insert(
601 STRUCTURAL_ENCODING_META_KEY.to_string(),
602 structural_encoding.into(),
603 );
604
605 let test_cases = TestCases::default()
606 .with_range(0..3)
607 .with_range(1..2)
608 .with_indices(vec![1])
609 .with_indices(vec![2])
610 .with_min_file_version(LanceFileVersion::V2_1);
611 check_round_trip_encoding_of_data(vec![Arc::new(list_arr)], &test_cases, field_metadata)
612 .await;
613 }
614
615 #[rstest]
616 #[test_log::test(tokio::test)]
617 async fn test_simple_two_page_list(
618 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
619 structural_encoding: &str,
620 ) {
621 let items_builder = Int64Builder::new();
625 let mut list_builder = ListBuilder::new(items_builder);
626 for i in 0..512 {
627 list_builder.append_value([Some(i), Some(i * 2)]);
628 }
629 let list_array_1 = list_builder.finish();
630
631 let items_builder = Int64Builder::new();
632 let mut list_builder = ListBuilder::new(items_builder);
633 for i in 0..512 {
634 let i = i + 512;
635 list_builder.append_value([Some(i), Some(i * 2)]);
636 }
637 let list_array_2 = list_builder.finish();
638
639 let mut metadata = HashMap::new();
640 metadata.insert(
641 STRUCTURAL_ENCODING_META_KEY.to_string(),
642 structural_encoding.into(),
643 );
644
645 let test_cases = TestCases::default()
646 .with_min_file_version(LanceFileVersion::V2_1)
647 .with_page_sizes(vec![100])
648 .with_range(800..900);
649 check_round_trip_encoding_of_data(
650 vec![Arc::new(list_array_1), Arc::new(list_array_2)],
651 &test_cases,
652 metadata,
653 )
654 .await;
655 }
656
657 #[test_log::test(tokio::test)]
658 async fn test_simple_large_list() {
659 let items_builder = Int32Builder::new();
660 let mut list_builder = LargeListBuilder::new(items_builder);
661 list_builder.append_value([Some(1), Some(2), Some(3)]);
662 list_builder.append_value([Some(4), Some(5)]);
663 list_builder.append_null();
664 list_builder.append_value([Some(6), Some(7), Some(8)]);
665 let list_array = list_builder.finish();
666
667 let test_cases = TestCases::default()
668 .with_range(0..2)
669 .with_range(0..3)
670 .with_range(1..3)
671 .with_indices(vec![1, 3]);
672 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, HashMap::new())
673 .await;
674 }
675
676 #[rstest]
677 #[test_log::test(tokio::test)]
678 async fn test_empty_lists(
679 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
680 structural_encoding: &str,
681 ) {
682 let mut field_metadata = HashMap::new();
683 field_metadata.insert(
684 STRUCTURAL_ENCODING_META_KEY.to_string(),
685 structural_encoding.into(),
686 );
687
688 let values = [vec![Some(1), Some(2), Some(3)], vec![], vec![None]];
691 for order in [[0, 1, 2], [1, 0, 2], [2, 0, 1]] {
693 let items_builder = Int32Builder::new();
694 let mut list_builder = ListBuilder::new(items_builder);
695 for idx in order {
696 list_builder.append_value(values[idx].clone());
697 }
698 let list_array = Arc::new(list_builder.finish());
699 let test_cases = TestCases::default()
700 .with_indices(vec![1])
701 .with_indices(vec![0])
702 .with_indices(vec![2])
703 .with_indices(vec![0, 1]);
704 check_round_trip_encoding_of_data(
705 vec![list_array.clone()],
706 &test_cases,
707 field_metadata.clone(),
708 )
709 .await;
710 let test_cases = test_cases.with_batch_size(1);
711 check_round_trip_encoding_of_data(
712 vec![list_array],
713 &test_cases,
714 field_metadata.clone(),
715 )
716 .await;
717 }
718
719 let items_builder = Int32Builder::new();
724 let mut list_builder = ListBuilder::new(items_builder);
725 list_builder.append(true);
726 list_builder.append_null();
727 list_builder.append(true);
728 let list_array = Arc::new(list_builder.finish());
729
730 let test_cases = TestCases::default().with_range(0..2).with_indices(vec![1]);
731 check_round_trip_encoding_of_data(
732 vec![list_array.clone()],
733 &test_cases,
734 field_metadata.clone(),
735 )
736 .await;
737 let test_cases = test_cases.with_batch_size(1);
738 check_round_trip_encoding_of_data(vec![list_array], &test_cases, field_metadata.clone())
739 .await;
740
741 let items_builder = StringBuilder::new();
746 let mut list_builder = ListBuilder::new(items_builder);
747 list_builder.append(true);
748 list_builder.append_null();
749 list_builder.append(true);
750 let list_array = Arc::new(list_builder.finish());
751
752 let test_cases = TestCases::default().with_range(0..2).with_indices(vec![1]);
753 check_round_trip_encoding_of_data(
754 vec![list_array.clone()],
755 &test_cases,
756 field_metadata.clone(),
757 )
758 .await;
759 let test_cases = test_cases.with_batch_size(1);
760 check_round_trip_encoding_of_data(vec![list_array], &test_cases, field_metadata.clone())
761 .await;
762
763 let items_builder = Int32Builder::new();
766 let mut list_builder = ListBuilder::new(items_builder);
767 list_builder.append_null();
768 list_builder.append_null();
769 list_builder.append_null();
770 let list_array = Arc::new(list_builder.finish());
771
772 let test_cases = TestCases::default().with_range(0..2).with_indices(vec![1]);
773 check_round_trip_encoding_of_data(
774 vec![list_array.clone()],
775 &test_cases,
776 field_metadata.clone(),
777 )
778 .await;
779 let test_cases = test_cases.with_batch_size(1);
780 check_round_trip_encoding_of_data(vec![list_array], &test_cases, field_metadata.clone())
781 .await;
782
783 let items_builder = Int32Builder::new();
786 let mut list_builder = ListBuilder::new(items_builder);
787 list_builder.append_null();
788 list_builder.append_null();
789 list_builder.append_null();
790 let list_array = Arc::new(list_builder.finish());
791
792 let struct_validity = NullBuffer::new(BooleanBuffer::from(vec![true, false, true]));
793 let struct_array = Arc::new(StructArray::new(
794 Fields::from(vec![Field::new(
795 "lists",
796 list_array.data_type().clone(),
797 true,
798 )]),
799 vec![list_array],
800 Some(struct_validity),
801 ));
802
803 let test_cases = TestCases::default()
804 .with_range(0..2)
805 .with_indices(vec![1])
806 .with_min_file_version(LanceFileVersion::V2_1);
807 check_round_trip_encoding_of_data(
808 vec![struct_array.clone()],
809 &test_cases,
810 field_metadata.clone(),
811 )
812 .await;
813 let test_cases = test_cases.with_batch_size(1);
814 check_round_trip_encoding_of_data(vec![struct_array], &test_cases, field_metadata.clone())
815 .await;
816 }
817
818 #[test_log::test(tokio::test)]
819 async fn test_empty_list_list() {
820 let items_builder = Int32Builder::new();
821 let list_builder = ListBuilder::new(items_builder);
822 let mut outer_list_builder = ListBuilder::new(list_builder);
823 outer_list_builder.append_null();
824 outer_list_builder.append_null();
825 outer_list_builder.append_null();
826 let list_array = Arc::new(outer_list_builder.finish());
827
828 let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1);
829 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
830 }
831
832 #[test_log::test(tokio::test)]
833 #[ignore] async fn test_jumbo_list() {
835 let items = BooleanArray::new_null(1024 * 1024);
839 let offsets = OffsetBuffer::new(ScalarBuffer::from(vec![0, 1024 * 1024]));
840 let list_arr = Arc::new(ListArray::new(
841 Arc::new(Field::new("item", DataType::Boolean, true)),
842 offsets,
843 Arc::new(items),
844 None,
845 )) as ArrayRef;
846 let arrs = vec![list_arr; 5000];
847
848 let test_cases = TestCases::default().without_validation();
850 check_round_trip_encoding_of_data(arrs, &test_cases, HashMap::new()).await;
851 }
852
853 #[tokio::test]
857 async fn test_fuzz_issue_4466() {
858 let list_sizes = vec![
861 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, ];
872
873 let mut list_builder = ListBuilder::new(Int32Builder::new());
875 let mut total_values = 0;
876
877 for size in &list_sizes {
878 for i in 0..*size {
879 list_builder.values().append_value(i);
880 }
881 list_builder.append(true);
882 total_values += size;
883 }
884
885 let list_array = Arc::new(list_builder.finish());
886
887 assert_eq!(list_array.len(), 95);
889 assert_eq!(total_values, 1025);
890
891 let test_cases = TestCases::default().with_min_file_version(LanceFileVersion::V2_1);
894
895 check_round_trip_encoding_of_data(vec![list_array], &test_cases, HashMap::new()).await;
899 }
900
901 #[rstest]
902 #[test_log::test(tokio::test)]
903 async fn test_sparse_large_string_list(
904 #[values(STRUCTURAL_ENCODING_MINIBLOCK, STRUCTURAL_ENCODING_FULLZIP)]
905 structural_encoding: &str,
906 ) {
907 let num_rows = 2_500_000u32;
909 let num_non_empty = 100u32;
910 let strings_per_list = 10;
911
912 let items_builder = StringBuilder::new();
913 let mut list_builder = ListBuilder::new(items_builder);
914
915 let step = num_rows / num_non_empty;
917 let mut next_non_empty = step / 2;
918
919 for i in 0..num_rows {
920 if i == next_non_empty {
921 let vals: Vec<Option<&str>> = (0..strings_per_list)
922 .map(|j| match j % 4 {
923 0 => Some("a"),
924 1 => Some("bb"),
925 2 => Some("ccc"),
926 _ => Some("d"),
927 })
928 .collect();
929 list_builder.append_value(vals);
930 next_non_empty = next_non_empty.saturating_add(step);
931 } else {
932 list_builder.append_value([] as [Option<&str>; 0]);
933 }
934 }
935 let list_array = list_builder.finish();
936
937 let mut field_metadata = HashMap::new();
938 field_metadata.insert(
939 STRUCTURAL_ENCODING_META_KEY.to_string(),
940 structural_encoding.into(),
941 );
942
943 let test_cases = TestCases::default()
944 .with_range(0..1000)
945 .with_range(0..num_rows as u64)
946 .with_indices(vec![0, (step / 2) as u64, num_rows as u64 - 1])
947 .with_max_file_version(LanceFileVersion::V2_2);
948 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
949 .await;
950 }
951
952 fn make_hnsw_shaped_list_u32() -> ListArray {
957 const DENSE_ROWS: u32 = 40_000;
958 const NEIGHBORS_PER_ROW: u32 = 32;
959 const EMPTY_TAIL_ROWS: u32 = 240_000;
960
961 let mut list_builder = ListBuilder::new(UInt32Builder::new());
962 let mut next_val: u32 = 0;
963 for _ in 0..DENSE_ROWS {
964 for _ in 0..NEIGHBORS_PER_ROW {
965 list_builder.values().append_value(next_val);
966 next_val = next_val.wrapping_add(1);
967 }
968 list_builder.append(true);
969 }
970 for _ in 0..EMPTY_TAIL_ROWS {
971 list_builder.append(true);
972 }
973 list_builder.finish()
974 }
975
976 #[test_log::test(tokio::test)]
985 async fn test_list_hnsw_shape_auto_routes_around_miniblock_overflow_v2_2() {
986 let list_array = make_hnsw_shaped_list_u32();
987 let dense_rows: u64 = 40_000;
988 let total_rows = list_array.len() as u64;
989
990 let field_metadata = HashMap::new();
991
992 let test_cases = TestCases::default()
993 .with_range(0..1000)
994 .with_range(dense_rows.saturating_sub(8)..(dense_rows + 8))
995 .with_range(0..total_rows)
996 .with_indices(vec![0, dense_rows - 1, dense_rows, total_rows - 1])
997 .with_min_file_version(LanceFileVersion::V2_2)
998 .with_max_file_version(LanceFileVersion::V2_2);
999 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
1000 .await;
1001 }
1002
1003 #[test_log::test(tokio::test)]
1015 async fn test_forced_miniblock_hnsw_shape_routed_to_fullzip_v2_2() {
1016 let list_array = make_hnsw_shaped_list_u32();
1017 let total_rows = list_array.len() as u64;
1018
1019 let mut field_metadata = HashMap::new();
1020 field_metadata.insert(
1021 STRUCTURAL_ENCODING_META_KEY.to_string(),
1022 STRUCTURAL_ENCODING_MINIBLOCK.into(),
1023 );
1024
1025 let test_cases = TestCases::default()
1026 .with_range(0..total_rows)
1027 .with_min_file_version(LanceFileVersion::V2_2)
1028 .with_max_file_version(LanceFileVersion::V2_2);
1029 check_round_trip_encoding_of_data(vec![Arc::new(list_array)], &test_cases, field_metadata)
1030 .await;
1031 }
1032}