1use std::collections::VecDeque;
216use std::sync::atomic::{AtomicU64, Ordering};
217use std::sync::{LazyLock, Once, OnceLock};
218use std::{ops::Range, sync::Arc};
219
220use arrow_array::cast::AsArray;
221use arrow_array::{ArrayRef, RecordBatch, RecordBatchIterator, RecordBatchReader};
222use arrow_schema::{ArrowError, DataType, Field as ArrowField, Fields, Schema as ArrowSchema};
223use bytes::Bytes;
224use futures::future::{BoxFuture, MaybeDone, maybe_done};
225use futures::stream::{self, BoxStream};
226use futures::{FutureExt, StreamExt};
227use lance_arrow::DataTypeExt;
228use lance_core::cache::{Context, DeepSizeOf, LanceCache};
229use lance_core::datatypes::{
230 BLOB_DESC_LANCE_FIELD, Field, Schema, validate_fixed_size_list_dimensions,
231};
232use lance_core::utils::futures::{FinallyStreamExt, StreamOnDropExt};
233use lance_core::utils::parse::parse_env_as_bool;
234use log::{debug, trace, warn};
235use prost::Message;
236use tokio::sync::mpsc::error::SendError;
237use tokio::sync::mpsc::{self, unbounded_channel};
238
239use lance_core::error::LanceOptionExt;
240use lance_core::{ArrowResult, Error, Result};
241use tracing::instrument;
242
243use crate::array_encoding::logical::list::OffsetPageInfo;
244use crate::array_encoding::logical::r#struct::{SimpleStructDecoder, SimpleStructScheduler};
245use crate::array_encoding::logical::{
246 binary::BinaryFieldScheduler, blob::BlobFieldScheduler, list::ListFieldScheduler,
247 primitive::PrimitiveFieldScheduler,
248};
249use crate::compression::{DecompressionStrategy, DefaultDecompressionStrategy};
250use crate::data::DataBlock;
251use crate::encoder::EncodedBatch;
252use crate::encodings::logical::fixed_size_list::StructuralFixedSizeListScheduler;
253use crate::encodings::logical::list::StructuralListScheduler;
254use crate::encodings::logical::map::StructuralMapScheduler;
255use crate::encodings::logical::primitive::StructuralPrimitiveFieldScheduler;
256use crate::encodings::logical::r#struct::{StructuralStructDecoder, StructuralStructScheduler};
257use crate::format::pb::{self, column_encoding};
258use crate::format::pb21;
259use crate::repdef::{CompositeRepDefUnraveler, RepDefUnraveler};
260use crate::{BufferScheduler, EncodingsIo};
261
262pub const CANDIDATE_BATCH_SIZES: [u32; 8] = [1, 4, 16, 64, 256, 1024, 4096, 16384];
265
266pub trait SchedulingJob: std::fmt::Debug {
267 fn schedule_next(
268 &mut self,
269 context: &mut SchedulerContext,
270 priority: &dyn PriorityRange,
271 ) -> Result<ScheduledScanLine>;
272
273 fn num_rows(&self) -> u64;
274}
275
276pub trait FieldScheduler: Send + Sync + std::fmt::Debug {
278 fn initialize<'a>(
279 &'a self,
280 filter: &'a FilterExpression,
281 context: &'a SchedulerContext,
282 ) -> BoxFuture<'a, Result<()>>;
283
284 fn schedule_ranges<'a>(
285 &'a self,
286 ranges: &[Range<u64>],
287 filter: &FilterExpression,
288 ) -> Result<Box<dyn SchedulingJob + 'a>>;
289
290 fn num_rows(&self) -> u64;
291}
292
293#[derive(Debug)]
294pub struct DecoderReady {
295 pub decoder: Box<dyn LogicalPageDecoder>,
296 pub path: VecDeque<u32>,
297}
298
299pub trait LogicalPageDecoder: std::fmt::Debug + Send {
301 fn accept_child(&mut self, _child: DecoderReady) -> Result<()> {
302 Err(Error::internal(format!(
303 "The decoder {:?} does not expect children but received a child",
304 self
305 )))
306 }
307
308 fn wait_for_loaded(&'_ mut self, loaded_need: u64) -> BoxFuture<'_, Result<()>>;
309
310 fn rows_loaded(&self) -> u64;
311
312 fn rows_unloaded(&self) -> u64 {
313 self.num_rows() - self.rows_loaded()
314 }
315
316 fn num_rows(&self) -> u64;
317
318 fn rows_drained(&self) -> u64;
319
320 fn rows_left(&self) -> u64 {
321 self.num_rows() - self.rows_drained()
322 }
323
324 fn drain(&mut self, num_rows: u64) -> Result<NextDecodeTask>;
325
326 fn data_type(&self) -> &DataType;
327}
328
329const BATCH_SIZE_BYTES_WARNING: u64 = 10 * 1024 * 1024;
331const ENV_LANCE_STRUCTURAL_BATCH_DECODE_SPAWN_MODE: &str =
332 "LANCE_STRUCTURAL_BATCH_DECODE_SPAWN_MODE";
333const ENV_LANCE_READ_CACHE_REPETITION_INDEX: &str = "LANCE_READ_CACHE_REPETITION_INDEX";
334const ENV_LANCE_INLINE_SCHEDULING_THRESHOLD: &str = "LANCE_INLINE_SCHEDULING_THRESHOLD";
335
336const DEFAULT_INLINE_SCHEDULING_THRESHOLD: u64 = 16 * 1024;
339
340fn default_cache_repetition_index() -> bool {
341 static DEFAULT_CACHE_REPETITION_INDEX: OnceLock<bool> = OnceLock::new();
342 *DEFAULT_CACHE_REPETITION_INDEX
343 .get_or_init(|| parse_env_as_bool(ENV_LANCE_READ_CACHE_REPETITION_INDEX, true))
344}
345
346fn inline_scheduling_threshold() -> u64 {
347 static THRESHOLD: OnceLock<u64> = OnceLock::new();
348 *THRESHOLD.get_or_init(|| {
349 std::env::var(ENV_LANCE_INLINE_SCHEDULING_THRESHOLD)
350 .ok()
351 .and_then(|v| v.trim().parse::<u64>().ok())
352 .unwrap_or(DEFAULT_INLINE_SCHEDULING_THRESHOLD)
353 })
354}
355
356#[derive(Debug, Clone)]
363pub enum PageEncoding {
364 Legacy(pb::ArrayEncoding),
365 Structural(pb21::PageLayout),
366}
367
368impl DeepSizeOf for PageEncoding {
369 fn deep_size_of_children(&self, _context: &mut Context) -> usize {
370 match self {
371 Self::Legacy(encoding) => encoding.encoded_len() * 4,
372 Self::Structural(encoding) => encoding.encoded_len() * 4,
373 }
374 }
375}
376
377impl PageEncoding {
378 pub fn as_legacy(&self) -> &pb::ArrayEncoding {
379 match self {
380 Self::Legacy(enc) => enc,
381 Self::Structural(_) => panic!("Expected a legacy encoding"),
382 }
383 }
384
385 pub fn as_structural(&self) -> &pb21::PageLayout {
386 match self {
387 Self::Structural(enc) => enc,
388 Self::Legacy(_) => panic!("Expected a structural encoding"),
389 }
390 }
391
392 pub fn is_structural(&self) -> bool {
393 matches!(self, Self::Structural(_))
394 }
395}
396
397#[derive(Debug)]
401pub struct PageInfo {
402 pub num_rows: u64,
404 pub priority: u64,
408 pub encoding: PageEncoding,
410 pub buffer_offsets_and_sizes: Arc<[(u64, u64)]>,
412}
413
414impl DeepSizeOf for PageInfo {
415 fn deep_size_of_children(&self, context: &mut Context) -> usize {
416 self.encoding.deep_size_of_children(context)
417 + self.buffer_offsets_and_sizes.deep_size_of_children(context)
418 }
419}
420
421#[derive(Debug, Clone)]
425pub struct ColumnInfo {
426 pub index: u32,
428 pub page_infos: Arc<[PageInfo]>,
430 pub buffer_offsets_and_sizes: Arc<[(u64, u64)]>,
432 pub encoding: pb::ColumnEncoding,
433}
434
435impl DeepSizeOf for ColumnInfo {
436 fn deep_size_of_children(&self, context: &mut Context) -> usize {
437 self.page_infos.deep_size_of_children(context)
438 + self.buffer_offsets_and_sizes.deep_size_of_children(context)
439 + self.encoding.encoded_len() * 4
440 }
441}
442
443impl ColumnInfo {
444 pub fn new(
446 index: u32,
447 page_infos: Arc<[PageInfo]>,
448 buffer_offsets_and_sizes: Vec<(u64, u64)>,
449 encoding: pb::ColumnEncoding,
450 ) -> Self {
451 Self {
452 index,
453 page_infos,
454 buffer_offsets_and_sizes: buffer_offsets_and_sizes.into_boxed_slice().into(),
455 encoding,
456 }
457 }
458
459 pub fn is_structural(&self) -> bool {
460 self.page_infos
461 .first()
463 .map(|page| page.encoding.is_structural())
464 .unwrap_or(false)
465 }
466}
467
468enum RootScheduler {
469 Structural(Box<dyn StructuralFieldScheduler>),
470 Array(Arc<dyn FieldScheduler>),
471}
472
473impl RootScheduler {
474 fn as_array(&self) -> &Arc<dyn FieldScheduler> {
475 match self {
476 Self::Structural(_) => panic!("Expected an array scheduler"),
477 Self::Array(s) => s,
478 }
479 }
480
481 fn as_structural(&self) -> &dyn StructuralFieldScheduler {
482 match self {
483 Self::Structural(s) => s.as_ref(),
484 Self::Array(_) => panic!("Expected a structural scheduler"),
485 }
486 }
487}
488
489pub struct DecodeBatchScheduler {
511 root_scheduler: RootScheduler,
512 pub root_fields: Fields,
513 cache: Arc<LanceCache>,
514}
515
516pub struct ColumnInfoIter<'a> {
517 column_infos: Vec<Arc<ColumnInfo>>,
518 column_indices: &'a [u32],
519 column_info_pos: usize,
520 column_indices_pos: usize,
521}
522
523impl<'a> ColumnInfoIter<'a> {
524 pub fn new(column_infos: Vec<Arc<ColumnInfo>>, column_indices: &'a [u32]) -> Self {
525 let initial_pos = column_indices.first().copied().unwrap_or(0) as usize;
526 Self {
527 column_infos,
528 column_indices,
529 column_info_pos: initial_pos,
530 column_indices_pos: 0,
531 }
532 }
533
534 pub fn peek(&self) -> &Arc<ColumnInfo> {
535 &self.column_infos[self.column_info_pos]
536 }
537
538 pub fn peek_transform(&mut self, transform: impl FnOnce(Arc<ColumnInfo>) -> Arc<ColumnInfo>) {
539 let column_info = self.column_infos[self.column_info_pos].clone();
540 let transformed = transform(column_info);
541 self.column_infos[self.column_info_pos] = transformed;
542 }
543
544 pub fn expect_next(&mut self) -> Result<&Arc<ColumnInfo>> {
545 self.next().ok_or_else(|| {
546 Error::invalid_input(
547 "there were more fields in the schema than provided column indices / infos",
548 )
549 })
550 }
551
552 fn next(&mut self) -> Option<&Arc<ColumnInfo>> {
553 if self.column_info_pos < self.column_infos.len() {
554 let info = &self.column_infos[self.column_info_pos];
555 self.column_info_pos += 1;
556 Some(info)
557 } else {
558 None
559 }
560 }
561
562 pub(crate) fn next_top_level(&mut self) {
563 self.column_indices_pos += 1;
564 if self.column_indices_pos < self.column_indices.len() {
565 self.column_info_pos = self.column_indices[self.column_indices_pos] as usize;
566 } else {
567 self.column_info_pos = self.column_infos.len();
568 }
569 }
570}
571
572#[derive(Clone, Copy, Debug)]
574pub struct FileBuffers<'a> {
575 pub positions_and_sizes: &'a [(u64, u64)],
576}
577
578#[derive(Clone, Copy, Debug)]
580pub struct ColumnBuffers<'a, 'b> {
581 pub file_buffers: FileBuffers<'a>,
582 pub positions_and_sizes: &'b [(u64, u64)],
583}
584
585#[derive(Clone, Copy, Debug)]
587pub struct PageBuffers<'a, 'b, 'c> {
588 pub column_buffers: ColumnBuffers<'a, 'b>,
589 pub positions_and_sizes: &'c [(u64, u64)],
590}
591
592#[derive(Debug)]
594pub struct CoreFieldDecoderStrategy {
595 pub validate_data: bool,
596 pub decompressor_strategy: Arc<dyn DecompressionStrategy>,
597 pub cache_repetition_index: bool,
598}
599
600impl Default for CoreFieldDecoderStrategy {
601 fn default() -> Self {
602 Self {
603 validate_data: false,
604 decompressor_strategy: Arc::new(DefaultDecompressionStrategy {}),
605 cache_repetition_index: false,
606 }
607 }
608}
609
610impl CoreFieldDecoderStrategy {
611 pub fn with_cache_repetition_index(mut self, cache_repetition_index: bool) -> Self {
613 self.cache_repetition_index = cache_repetition_index;
614 self
615 }
616
617 pub fn from_decoder_config(config: &DecoderConfig) -> Self {
619 Self {
620 validate_data: config.validate_on_decode,
621 decompressor_strategy: Arc::new(DefaultDecompressionStrategy {}),
622 cache_repetition_index: config.cache_repetition_index,
623 }
624 }
625
626 fn ensure_values_encoded(column_info: &ColumnInfo, field_name: &str) -> Result<()> {
629 let column_encoding = column_info
630 .encoding
631 .column_encoding
632 .as_ref()
633 .ok_or_else(|| {
634 Error::invalid_input(format!(
635 "the column at index {} was missing a ColumnEncoding",
636 column_info.index
637 ))
638 })?;
639 if matches!(
640 column_encoding,
641 pb::column_encoding::ColumnEncoding::Values(_)
642 ) {
643 Ok(())
644 } else {
645 Err(Error::invalid_input(format!(
646 "the column at index {} mapping to the input field {} has column encoding {:?} and no decoder is registered to handle it",
647 column_info.index, field_name, column_encoding
648 )))
649 }
650 }
651
652 fn is_structural_primitive(data_type: &DataType) -> bool {
653 if data_type.is_primitive() {
654 true
655 } else {
656 match data_type {
657 DataType::Dictionary(_, value_type) => Self::is_structural_primitive(value_type),
659 DataType::Boolean
660 | DataType::Null
661 | DataType::FixedSizeBinary(_)
662 | DataType::Binary
663 | DataType::LargeBinary
664 | DataType::Utf8
665 | DataType::LargeUtf8 => true,
666 DataType::FixedSizeList(inner, _) => {
667 Self::is_structural_primitive(inner.data_type())
668 }
669 _ => false,
670 }
671 }
672 }
673
674 fn is_array_primitive(data_type: &DataType) -> bool {
675 if data_type.is_primitive() {
676 true
677 } else {
678 match data_type {
679 DataType::Boolean | DataType::Null | DataType::FixedSizeBinary(_) => true,
681 DataType::FixedSizeList(inner, _) => Self::is_array_primitive(inner.data_type()),
682 _ => false,
683 }
684 }
685 }
686
687 fn create_primitive_scheduler(
688 &self,
689 field: &Field,
690 column: &ColumnInfo,
691 buffers: FileBuffers,
692 ) -> Result<Box<dyn FieldScheduler>> {
693 Self::ensure_values_encoded(column, &field.name)?;
694 let column_buffers = ColumnBuffers {
696 file_buffers: buffers,
697 positions_and_sizes: &column.buffer_offsets_and_sizes,
698 };
699 Ok(Box::new(PrimitiveFieldScheduler::new(
700 column.index,
701 field.data_type(),
702 column.page_infos.clone(),
703 column_buffers,
704 self.validate_data,
705 )))
706 }
707
708 fn check_simple_struct(column_info: &ColumnInfo, field_name: &str) -> Result<()> {
710 Self::ensure_values_encoded(column_info, field_name)?;
711 if column_info.page_infos.len() != 1 {
712 return Err(Error::invalid_input_source(format!("Due to schema we expected a struct column but we received a column with {} pages and right now we only support struct columns with 1 page", column_info.page_infos.len()).into()));
713 }
714 let encoding = &column_info.page_infos[0].encoding;
715 match encoding.as_legacy().array_encoding.as_ref().unwrap() {
716 pb::array_encoding::ArrayEncoding::Struct(_) => Ok(()),
717 _ => Err(Error::invalid_input_source(format!("Expected a struct encoding because we have a struct field in the schema but got the encoding {:?}", encoding).into())),
718 }
719 }
720
721 fn check_packed_struct(column_info: &ColumnInfo) -> bool {
722 let encoding = &column_info.page_infos[0].encoding;
723 matches!(
724 encoding.as_legacy().array_encoding.as_ref().unwrap(),
725 pb::array_encoding::ArrayEncoding::PackedStruct(_)
726 )
727 }
728
729 fn create_list_scheduler(
730 &self,
731 list_field: &Field,
732 column_infos: &mut ColumnInfoIter,
733 buffers: FileBuffers,
734 offsets_column: &ColumnInfo,
735 ) -> Result<Box<dyn FieldScheduler>> {
736 Self::ensure_values_encoded(offsets_column, &list_field.name)?;
737 let offsets_column_buffers = ColumnBuffers {
738 file_buffers: buffers,
739 positions_and_sizes: &offsets_column.buffer_offsets_and_sizes,
740 };
741 let items_scheduler =
742 self.create_array_field_scheduler(&list_field.children[0], column_infos, buffers)?;
743
744 let mut inner_infos = Vec::with_capacity(offsets_column.page_infos.len());
745 let mut null_offset_adjustments = Vec::with_capacity(offsets_column.page_infos.len());
746 for (page_index, offsets_page) in offsets_column
747 .page_infos
748 .iter()
749 .enumerate()
750 .filter(|(_, offsets_page)| offsets_page.num_rows > 0)
751 {
752 let PageEncoding::Legacy(pb::ArrayEncoding {
753 array_encoding: Some(pb::array_encoding::ArrayEncoding::List(list_encoding)),
754 }) = &offsets_page.encoding
755 else {
756 return Err(Error::invalid_input(format!(
757 "expected list encoding for field '{}' in column {}, page {} but got {:?}",
758 list_field.name, offsets_column.index, page_index, offsets_page.encoding
759 )));
760 };
761 let offsets_encoding = list_encoding.offsets.as_ref().ok_or_else(|| {
762 Error::invalid_input(format!(
763 "list encoding for field '{}' in column {}, page {} is missing its offsets encoding",
764 list_field.name, offsets_column.index, page_index
765 ))
766 })?;
767 inner_infos.push(PageInfo {
768 buffer_offsets_and_sizes: offsets_page.buffer_offsets_and_sizes.clone(),
769 encoding: PageEncoding::Legacy(offsets_encoding.as_ref().clone()),
770 num_rows: offsets_page.num_rows,
771 priority: 0,
772 });
773 null_offset_adjustments.push(OffsetPageInfo {
774 offsets_in_page: offsets_page.num_rows,
775 null_offset_adjustment: list_encoding.null_offset_adjustment,
776 num_items_referenced_by_page: list_encoding.num_items,
777 });
778 }
779 let inner = Arc::new(PrimitiveFieldScheduler::new(
780 offsets_column.index,
781 DataType::UInt64,
782 Arc::from(inner_infos.into_boxed_slice()),
783 offsets_column_buffers,
784 self.validate_data,
785 )) as Arc<dyn FieldScheduler>;
786 let items_field = match list_field.data_type() {
787 DataType::List(inner) => inner,
788 DataType::LargeList(inner) => inner,
789 _ => unreachable!(),
790 };
791 let offset_type = if matches!(list_field.data_type(), DataType::List(_)) {
792 DataType::Int32
793 } else {
794 DataType::Int64
795 };
796 Ok(Box::new(ListFieldScheduler::new(
797 inner,
798 items_scheduler.into(),
799 items_field,
800 offset_type,
801 null_offset_adjustments,
802 )))
803 }
804
805 fn unwrap_blob(column_info: &ColumnInfo) -> Option<ColumnInfo> {
806 if let column_encoding::ColumnEncoding::Blob(blob) =
807 column_info.encoding.column_encoding.as_ref().unwrap()
808 {
809 let mut column_info = column_info.clone();
810 column_info.encoding = blob.inner.as_ref().unwrap().as_ref().clone();
811 Some(column_info)
812 } else {
813 None
814 }
815 }
816
817 fn create_structural_field_scheduler(
818 &self,
819 field: &Field,
820 column_infos: &mut ColumnInfoIter,
821 ) -> Result<Box<dyn StructuralFieldScheduler>> {
822 let data_type = field.data_type();
823 validate_fixed_size_list_dimensions(&field.name, &data_type)?;
824 if Self::is_structural_primitive(&data_type) {
825 let column_info = column_infos.expect_next()?;
826 let scheduler = Box::new(StructuralPrimitiveFieldScheduler::try_new(
827 column_info.as_ref(),
828 self.decompressor_strategy.as_ref(),
829 self.cache_repetition_index,
830 field,
831 )?);
832
833 column_infos.next_top_level();
835
836 return Ok(scheduler);
837 }
838 match &data_type {
839 DataType::Struct(fields) => {
840 if field.is_packed_struct() {
841 let column_info = column_infos.expect_next()?;
843 let scheduler = Box::new(StructuralPrimitiveFieldScheduler::try_new(
844 column_info.as_ref(),
845 self.decompressor_strategy.as_ref(),
846 self.cache_repetition_index,
847 field,
848 )?);
849
850 column_infos.next_top_level();
852
853 return Ok(scheduler);
854 }
855 if field.is_blob() {
857 let column_info = column_infos.peek();
858 if column_info.page_infos.iter().any(|page| {
859 matches!(
860 page.encoding,
861 PageEncoding::Structural(pb21::PageLayout {
862 layout: Some(pb21::page_layout::Layout::BlobLayout(_))
863 })
864 )
865 }) {
866 let column_info = column_infos.expect_next()?;
867 let scheduler = Box::new(StructuralPrimitiveFieldScheduler::try_new(
868 column_info.as_ref(),
869 self.decompressor_strategy.as_ref(),
870 self.cache_repetition_index,
871 field,
872 )?);
873 column_infos.next_top_level();
874 return Ok(scheduler);
875 }
876 }
877
878 let mut child_schedulers = Vec::with_capacity(field.children.len());
879 for field in field.children.iter() {
880 let field_scheduler =
881 self.create_structural_field_scheduler(field, column_infos)?;
882 child_schedulers.push(field_scheduler);
883 }
884
885 let fields = fields.clone();
886 Ok(
887 Box::new(StructuralStructScheduler::new(child_schedulers, fields))
888 as Box<dyn StructuralFieldScheduler>,
889 )
890 }
891 DataType::List(_) | DataType::LargeList(_) => {
892 let child = field.children.first().expect_ok()?;
893 let child_scheduler =
894 self.create_structural_field_scheduler(child, column_infos)?;
895 Ok(Box::new(StructuralListScheduler::new(child_scheduler))
896 as Box<dyn StructuralFieldScheduler>)
897 }
898 DataType::FixedSizeList(inner, dimension)
899 if matches!(inner.data_type(), DataType::Struct(_)) =>
900 {
901 let child = field.children.first().expect_ok()?;
902 let child_scheduler =
903 self.create_structural_field_scheduler(child, column_infos)?;
904 Ok(Box::new(StructuralFixedSizeListScheduler::new(
905 child_scheduler,
906 *dimension,
907 )) as Box<dyn StructuralFieldScheduler>)
908 }
909 DataType::Map(_, keys_sorted) => {
910 if *keys_sorted {
914 return Err(Error::not_supported_source(format!("Map data type is not supported with keys_sorted=true now, current value is {}", *keys_sorted).into()));
915 }
916 let entries_child = field.children.first().expect_ok()?;
917 let child_scheduler =
918 self.create_structural_field_scheduler(entries_child, column_infos)?;
919 Ok(Box::new(StructuralMapScheduler::new(child_scheduler))
920 as Box<dyn StructuralFieldScheduler>)
921 }
922 _ => todo!("create_structural_field_scheduler for {}", data_type),
923 }
924 }
925
926 fn create_array_field_scheduler(
927 &self,
928 field: &Field,
929 column_infos: &mut ColumnInfoIter,
930 buffers: FileBuffers,
931 ) -> Result<Box<dyn FieldScheduler>> {
932 let data_type = field.data_type();
933 validate_fixed_size_list_dimensions(&field.name, &data_type)?;
934 if Self::is_array_primitive(&data_type) {
935 let column_info = column_infos.expect_next()?;
936 let scheduler = self.create_primitive_scheduler(field, column_info, buffers)?;
937 return Ok(scheduler);
938 } else if data_type.is_binary_like() {
939 let column_info = column_infos.expect_next()?.clone();
940 if let Some(blob_col) = Self::unwrap_blob(column_info.as_ref()) {
942 let desc_scheduler =
943 self.create_primitive_scheduler(&BLOB_DESC_LANCE_FIELD, &blob_col, buffers)?;
944 let blob_scheduler = Box::new(BlobFieldScheduler::new(desc_scheduler.into()));
945 return Ok(blob_scheduler);
946 }
947 if let Some(page_info) = column_info.page_infos.first() {
948 if matches!(
949 page_info.encoding.as_legacy(),
950 pb::ArrayEncoding {
951 array_encoding: Some(pb::array_encoding::ArrayEncoding::List(..))
952 }
953 ) {
954 let list_type = if matches!(data_type, DataType::Utf8 | DataType::Binary) {
955 DataType::List(Arc::new(ArrowField::new("item", DataType::UInt8, false)))
956 } else {
957 DataType::LargeList(Arc::new(ArrowField::new(
958 "item",
959 DataType::UInt8,
960 false,
961 )))
962 };
963 let list_field = Field::try_from(ArrowField::new(
964 field.name.clone(),
965 list_type,
966 field.nullable,
967 ))
968 .unwrap();
969 let list_scheduler = self.create_list_scheduler(
970 &list_field,
971 column_infos,
972 buffers,
973 &column_info,
974 )?;
975 let binary_scheduler = Box::new(BinaryFieldScheduler::new(
976 list_scheduler.into(),
977 field.data_type(),
978 ));
979 return Ok(binary_scheduler);
980 } else {
981 let scheduler =
982 self.create_primitive_scheduler(field, &column_info, buffers)?;
983 return Ok(scheduler);
984 }
985 } else {
986 return self.create_primitive_scheduler(field, &column_info, buffers);
987 }
988 }
989 match &data_type {
990 DataType::FixedSizeList(inner, _dimension) => {
991 if Self::is_array_primitive(inner.data_type()) {
994 let primitive_col = column_infos.expect_next()?;
995 let scheduler =
996 self.create_primitive_scheduler(field, primitive_col, buffers)?;
997 Ok(scheduler)
998 } else {
999 todo!()
1000 }
1001 }
1002 DataType::Dictionary(_key_type, value_type) => {
1003 if Self::is_array_primitive(value_type) || value_type.is_binary_like() {
1004 let primitive_col = column_infos.expect_next()?;
1005 let scheduler =
1006 self.create_primitive_scheduler(field, primitive_col, buffers)?;
1007 Ok(scheduler)
1008 } else {
1009 Err(Error::not_supported_source(
1010 format!(
1011 "No way to decode into a dictionary field of type {}",
1012 value_type
1013 )
1014 .into(),
1015 ))
1016 }
1017 }
1018 DataType::List(_) | DataType::LargeList(_) => {
1019 let offsets_column = column_infos.expect_next()?.clone();
1020 column_infos.next_top_level();
1021 self.create_list_scheduler(field, column_infos, buffers, &offsets_column)
1022 }
1023 DataType::Struct(fields) => {
1024 let column_info = column_infos.expect_next()?;
1025
1026 if let Some(blob_col) = Self::unwrap_blob(column_info.as_ref()) {
1028 return self.create_primitive_scheduler(field, &blob_col, buffers);
1030 }
1031
1032 if Self::check_packed_struct(column_info) {
1033 self.create_primitive_scheduler(field, column_info, buffers)
1035 } else {
1036 Self::check_simple_struct(column_info, &field.name).unwrap();
1038 let num_rows = column_info
1039 .page_infos
1040 .iter()
1041 .map(|page| page.num_rows)
1042 .sum();
1043 let mut child_schedulers = Vec::with_capacity(field.children.len());
1044 for field in &field.children {
1045 column_infos.next_top_level();
1046 let field_scheduler =
1047 self.create_array_field_scheduler(field, column_infos, buffers)?;
1048 child_schedulers.push(Arc::from(field_scheduler));
1049 }
1050
1051 let fields = fields.clone();
1052 Ok(Box::new(SimpleStructScheduler::new(
1053 child_schedulers,
1054 fields,
1055 num_rows,
1056 )))
1057 }
1058 }
1059 _ => todo!(),
1061 }
1062 }
1063}
1064
1065fn root_column(num_rows: u64) -> ColumnInfo {
1067 let num_root_pages = num_rows.div_ceil(u32::MAX as u64);
1068 let final_page_num_rows = num_rows % (u32::MAX as u64);
1069 let root_pages = (0..num_root_pages)
1070 .map(|i| PageInfo {
1071 num_rows: if i == num_root_pages - 1 {
1072 final_page_num_rows
1073 } else {
1074 u64::MAX
1075 },
1076 encoding: PageEncoding::Legacy(pb::ArrayEncoding {
1077 array_encoding: Some(pb::array_encoding::ArrayEncoding::Struct(
1078 pb::SimpleStruct {},
1079 )),
1080 }),
1081 priority: 0, buffer_offsets_and_sizes: Arc::new([]),
1083 })
1084 .collect::<Vec<_>>();
1085 ColumnInfo {
1086 buffer_offsets_and_sizes: Arc::new([]),
1087 encoding: pb::ColumnEncoding {
1088 column_encoding: Some(pb::column_encoding::ColumnEncoding::Values(())),
1089 },
1090 index: u32::MAX,
1091 page_infos: Arc::from(root_pages),
1092 }
1093}
1094
1095pub enum RootDecoder {
1096 Structural(StructuralStructDecoder),
1097 Array(SimpleStructDecoder),
1098}
1099
1100impl RootDecoder {
1101 pub fn into_structural(self) -> StructuralStructDecoder {
1102 match self {
1103 Self::Structural(decoder) => decoder,
1104 Self::Array(_) => panic!("Expected a structural decoder"),
1105 }
1106 }
1107
1108 pub fn into_array(self) -> SimpleStructDecoder {
1109 match self {
1110 Self::Array(decoder) => decoder,
1111 Self::Structural(_) => panic!("Expected an array decoder"),
1112 }
1113 }
1114}
1115
1116impl DecodeBatchScheduler {
1117 #[allow(clippy::too_many_arguments)]
1120 pub async fn try_new<'a>(
1121 schema: &'a Schema,
1122 column_indices: &[u32],
1123 column_infos: &[Arc<ColumnInfo>],
1124 file_buffer_positions_and_sizes: &'a Vec<(u64, u64)>,
1125 num_rows: u64,
1126 _decoder_plugins: Arc<DecoderPlugins>,
1127 io: Arc<dyn EncodingsIo>,
1128 cache: Arc<LanceCache>,
1129 filter: &FilterExpression,
1130 decoder_config: &DecoderConfig,
1131 ) -> Result<Self> {
1132 assert!(num_rows > 0);
1133 let buffers = FileBuffers {
1134 positions_and_sizes: file_buffer_positions_and_sizes,
1135 };
1136 let arrow_schema = ArrowSchema::from(schema);
1137 let root_fields = arrow_schema.fields().clone();
1138 let root_type = DataType::Struct(root_fields.clone());
1139 let mut root_field = Field::try_from(&ArrowField::new("root", root_type, false))?;
1140 root_field.children.clone_from(&schema.fields);
1144 root_field
1145 .metadata
1146 .insert("__lance_decoder_root".to_string(), "true".to_string());
1147
1148 if column_infos.is_empty() || column_infos[0].is_structural() {
1149 let mut column_iter = ColumnInfoIter::new(column_infos.to_vec(), column_indices);
1150
1151 let strategy = CoreFieldDecoderStrategy::from_decoder_config(decoder_config);
1152 let mut root_scheduler =
1153 strategy.create_structural_field_scheduler(&root_field, &mut column_iter)?;
1154
1155 let context = SchedulerContext::new(io, cache.clone());
1156 root_scheduler.initialize(filter, &context).await?;
1157
1158 Ok(Self {
1159 root_scheduler: RootScheduler::Structural(root_scheduler),
1160 root_fields,
1161 cache,
1162 })
1163 } else {
1164 let mut columns = Vec::with_capacity(column_infos.len() + 1);
1167 columns.push(Arc::new(root_column(num_rows)));
1168 columns.extend(column_infos.iter().cloned());
1169
1170 let adjusted_column_indices = [0_u32]
1171 .into_iter()
1172 .chain(column_indices.iter().map(|i| i.saturating_add(1)))
1173 .collect::<Vec<_>>();
1174 let mut column_iter = ColumnInfoIter::new(columns, &adjusted_column_indices);
1175 let strategy = CoreFieldDecoderStrategy::from_decoder_config(decoder_config);
1176 let root_scheduler =
1177 strategy.create_array_field_scheduler(&root_field, &mut column_iter, buffers)?;
1178
1179 let context = SchedulerContext::new(io, cache.clone());
1180 root_scheduler.initialize(filter, &context).await?;
1181
1182 Ok(Self {
1183 root_scheduler: RootScheduler::Array(root_scheduler.into()),
1184 root_fields,
1185 cache,
1186 })
1187 }
1188 }
1189
1190 #[deprecated(since = "0.29.1", note = "This is for v2.0 array-encoding paths")]
1191 pub fn from_scheduler(
1192 root_scheduler: Arc<dyn FieldScheduler>,
1193 root_fields: Fields,
1194 cache: Arc<LanceCache>,
1195 ) -> Self {
1196 Self {
1197 root_scheduler: RootScheduler::Array(root_scheduler),
1198 root_fields,
1199 cache,
1200 }
1201 }
1202
1203 fn do_schedule_ranges_structural(
1204 &mut self,
1205 ranges: &[Range<u64>],
1206 filter: &FilterExpression,
1207 io: Arc<dyn EncodingsIo>,
1208 mut schedule_action: impl FnMut(Result<DecoderMessage>) -> bool,
1209 ) {
1210 let root_scheduler = self.root_scheduler.as_structural();
1211 let mut context = SchedulerContext::new(io, self.cache.clone());
1212 let maybe_root_job = root_scheduler.schedule_ranges(ranges, filter);
1213 if let Err(schedule_ranges_err) = maybe_root_job {
1214 schedule_action(Err(schedule_ranges_err));
1215 return;
1216 }
1217 let mut root_job = maybe_root_job.unwrap();
1218 let mut num_rows_scheduled = 0;
1219 loop {
1220 let maybe_next_scan_lines = root_job.schedule_next(&mut context);
1221 if let Err(err) = maybe_next_scan_lines {
1222 schedule_action(Err(err));
1223 return;
1224 }
1225 let next_scan_lines = maybe_next_scan_lines.unwrap();
1226 if next_scan_lines.is_empty() {
1227 return;
1228 }
1229 for next_scan_line in next_scan_lines {
1230 trace!(
1231 "Scheduled scan line of {} rows and {} decoders",
1232 next_scan_line.rows_scheduled,
1233 next_scan_line.decoders.len()
1234 );
1235 num_rows_scheduled += next_scan_line.rows_scheduled;
1236 if !schedule_action(Ok(DecoderMessage {
1237 scheduled_so_far: num_rows_scheduled,
1238 decoders: next_scan_line.decoders,
1239 })) {
1240 return;
1242 }
1243 }
1244 }
1245 }
1246
1247 fn do_schedule_ranges_array(
1248 &mut self,
1249 ranges: &[Range<u64>],
1250 filter: &FilterExpression,
1251 io: Arc<dyn EncodingsIo>,
1252 mut schedule_action: impl FnMut(Result<DecoderMessage>) -> bool,
1253 priority: Option<Box<dyn PriorityRange>>,
1257 ) {
1258 let root_scheduler = self.root_scheduler.as_array();
1259 let rows_requested = ranges.iter().map(|r| r.end - r.start).sum::<u64>();
1260 trace!(
1261 "Scheduling {} ranges across {}..{} ({} rows){}",
1262 ranges.len(),
1263 ranges.first().unwrap().start,
1264 ranges.last().unwrap().end,
1265 rows_requested,
1266 priority
1267 .as_ref()
1268 .map(|p| format!(" (priority={:?})", p))
1269 .unwrap_or_default()
1270 );
1271
1272 let mut context = SchedulerContext::new(io, self.cache.clone());
1273 let maybe_root_job = root_scheduler.schedule_ranges(ranges, filter);
1274 if let Err(schedule_ranges_err) = maybe_root_job {
1275 schedule_action(Err(schedule_ranges_err));
1276 return;
1277 }
1278 let mut root_job = maybe_root_job.unwrap();
1279 let mut num_rows_scheduled = 0;
1280 let mut rows_to_schedule = root_job.num_rows();
1281 let mut priority = priority.unwrap_or(Box::new(SimplePriorityRange::new(0)));
1282 trace!("Scheduled ranges refined to {} rows", rows_to_schedule);
1283 while rows_to_schedule > 0 {
1284 let maybe_next_scan_line = root_job.schedule_next(&mut context, priority.as_ref());
1285 if let Err(schedule_next_err) = maybe_next_scan_line {
1286 schedule_action(Err(schedule_next_err));
1287 return;
1288 }
1289 let next_scan_line = maybe_next_scan_line.unwrap();
1290 priority.advance(next_scan_line.rows_scheduled);
1291 num_rows_scheduled += next_scan_line.rows_scheduled;
1292 rows_to_schedule -= next_scan_line.rows_scheduled;
1293 trace!(
1294 "Scheduled scan line of {} rows and {} decoders",
1295 next_scan_line.rows_scheduled,
1296 next_scan_line.decoders.len()
1297 );
1298 if !schedule_action(Ok(DecoderMessage {
1299 scheduled_so_far: num_rows_scheduled,
1300 decoders: next_scan_line.decoders,
1301 })) {
1302 return;
1304 }
1305
1306 trace!("Finished scheduling {} ranges", ranges.len());
1307 }
1308 }
1309
1310 fn do_schedule_ranges(
1311 &mut self,
1312 ranges: &[Range<u64>],
1313 filter: &FilterExpression,
1314 io: Arc<dyn EncodingsIo>,
1315 schedule_action: impl FnMut(Result<DecoderMessage>) -> bool,
1316 priority: Option<Box<dyn PriorityRange>>,
1320 ) {
1321 match &self.root_scheduler {
1322 RootScheduler::Array(_) => {
1323 self.do_schedule_ranges_array(ranges, filter, io, schedule_action, priority)
1324 }
1325 RootScheduler::Structural(_) => {
1326 self.do_schedule_ranges_structural(ranges, filter, io, schedule_action)
1327 }
1328 }
1329 }
1330
1331 pub fn schedule_ranges_to_vec(
1334 &mut self,
1335 ranges: &[Range<u64>],
1336 filter: &FilterExpression,
1337 io: Arc<dyn EncodingsIo>,
1338 priority: Option<Box<dyn PriorityRange>>,
1339 ) -> Result<Vec<DecoderMessage>> {
1340 let mut decode_messages = Vec::new();
1341 self.do_schedule_ranges(
1342 ranges,
1343 filter,
1344 io,
1345 |msg| {
1346 decode_messages.push(msg);
1347 true
1348 },
1349 priority,
1350 );
1351 decode_messages.into_iter().collect::<Result<Vec<_>>>()
1352 }
1353
1354 #[instrument(level = "debug", skip_all)]
1364 pub fn schedule_ranges(
1365 &mut self,
1366 ranges: &[Range<u64>],
1367 filter: &FilterExpression,
1368 sink: mpsc::UnboundedSender<Result<DecoderMessage>>,
1369 scheduler: Arc<dyn EncodingsIo>,
1370 ) {
1371 self.do_schedule_ranges(
1372 ranges,
1373 filter,
1374 scheduler,
1375 |msg| {
1376 match sink.send(msg) {
1377 Ok(_) => true,
1378 Err(SendError { .. }) => {
1379 debug!(
1382 "schedule_ranges aborting early since decoder appears to have been dropped"
1383 );
1384 false
1385 }
1386 }
1387 },
1388 None,
1389 )
1390 }
1391
1392 #[instrument(level = "debug", skip_all)]
1400 pub fn schedule_range(
1401 &mut self,
1402 range: Range<u64>,
1403 filter: &FilterExpression,
1404 sink: mpsc::UnboundedSender<Result<DecoderMessage>>,
1405 scheduler: Arc<dyn EncodingsIo>,
1406 ) {
1407 self.schedule_ranges(&[range], filter, sink, scheduler)
1408 }
1409
1410 pub fn schedule_take(
1418 &mut self,
1419 indices: &[u64],
1420 filter: &FilterExpression,
1421 sink: mpsc::UnboundedSender<Result<DecoderMessage>>,
1422 scheduler: Arc<dyn EncodingsIo>,
1423 ) {
1424 debug_assert!(indices.windows(2).all(|w| w[0] < w[1]));
1425 if indices.is_empty() {
1426 return;
1427 }
1428 trace!("Scheduling take of {} rows", indices.len());
1429 let ranges = Self::indices_to_ranges(indices);
1430 self.schedule_ranges(&ranges, filter, sink, scheduler)
1431 }
1432
1433 fn indices_to_ranges(indices: &[u64]) -> Vec<Range<u64>> {
1435 let mut ranges = Vec::new();
1436 let mut start = indices[0];
1437
1438 for window in indices.windows(2) {
1439 if window[1] != window[0] + 1 {
1440 ranges.push(start..window[0] + 1);
1441 start = window[1];
1442 }
1443 }
1444
1445 ranges.push(start..*indices.last().unwrap() + 1);
1446 ranges
1447 }
1448}
1449
1450pub struct ReadBatchTask {
1451 pub task: BoxFuture<'static, Result<RecordBatch>>,
1452 pub num_rows: u32,
1453}
1454
1455pub struct BatchDecodeStream {
1457 context: DecoderContext,
1458 root_decoder: SimpleStructDecoder,
1459 rows_remaining: u64,
1460 rows_per_batch: u32,
1461 rows_scheduled: u64,
1462 rows_drained: u64,
1463 scheduler_exhausted: bool,
1464 emitted_batch_size_warning: Arc<Once>,
1465}
1466
1467impl BatchDecodeStream {
1468 pub fn new(
1478 scheduled: mpsc::UnboundedReceiver<Result<DecoderMessage>>,
1479 rows_per_batch: u32,
1480 num_rows: u64,
1481 root_decoder: SimpleStructDecoder,
1482 ) -> Self {
1483 Self {
1484 context: DecoderContext::new(scheduled),
1485 root_decoder,
1486 rows_remaining: num_rows,
1487 rows_per_batch,
1488 rows_scheduled: 0,
1489 rows_drained: 0,
1490 scheduler_exhausted: false,
1491 emitted_batch_size_warning: Arc::new(Once::new()),
1492 }
1493 }
1494
1495 fn accept_decoder(&mut self, decoder: DecoderReady) -> Result<()> {
1496 if decoder.path.is_empty() {
1497 Ok(())
1499 } else {
1500 self.root_decoder.accept_child(decoder)
1501 }
1502 }
1503
1504 #[instrument(level = "debug", skip_all)]
1505 async fn wait_for_scheduled(&mut self, scheduled_need: u64) -> Result<u64> {
1506 if self.scheduler_exhausted {
1507 return Ok(self.rows_scheduled);
1508 }
1509 while self.rows_scheduled < scheduled_need {
1510 let next_message = self.context.source.recv().await;
1511 match next_message {
1512 Some(scan_line) => {
1513 let scan_line = scan_line?;
1514 self.rows_scheduled = scan_line.scheduled_so_far;
1515 for message in scan_line.decoders {
1516 self.accept_decoder(message.into_array())?;
1517 }
1518 }
1519 None => {
1520 self.scheduler_exhausted = true;
1524 return Ok(self.rows_scheduled);
1525 }
1526 }
1527 }
1528 Ok(scheduled_need)
1529 }
1530
1531 #[instrument(level = "debug", skip_all)]
1532 async fn next_batch_task(&mut self) -> Result<Option<NextDecodeTask>> {
1533 trace!(
1534 "Draining batch task (rows_remaining={} rows_drained={} rows_scheduled={})",
1535 self.rows_remaining, self.rows_drained, self.rows_scheduled,
1536 );
1537 if self.rows_remaining == 0 {
1538 return Ok(None);
1539 }
1540
1541 let mut to_take = self.rows_remaining.min(self.rows_per_batch as u64);
1542 self.rows_remaining -= to_take;
1543
1544 let scheduled_need = (self.rows_drained + to_take).saturating_sub(self.rows_scheduled);
1545 trace!(
1546 "scheduled_need = {} because rows_drained = {} and to_take = {} and rows_scheduled = {}",
1547 scheduled_need, self.rows_drained, to_take, self.rows_scheduled
1548 );
1549 if scheduled_need > 0 {
1550 let desired_scheduled = scheduled_need + self.rows_scheduled;
1551 trace!(
1552 "Draining from scheduler (desire at least {} scheduled rows)",
1553 desired_scheduled
1554 );
1555 let actually_scheduled = self.wait_for_scheduled(desired_scheduled).await?;
1556 if actually_scheduled < desired_scheduled {
1557 let under_scheduled = desired_scheduled - actually_scheduled;
1558 to_take -= under_scheduled;
1559 }
1560 }
1561
1562 if to_take == 0 {
1563 return Ok(None);
1564 }
1565
1566 let loaded_need = self.rows_drained + to_take - 1;
1568 trace!(
1569 "Waiting for I/O (desire at least {} fully loaded rows)",
1570 loaded_need
1571 );
1572 self.root_decoder.wait_for_loaded(loaded_need).await?;
1573
1574 let next_task = self.root_decoder.drain(to_take)?;
1575 self.rows_drained += to_take;
1576 Ok(Some(next_task))
1577 }
1578
1579 pub fn into_stream(self) -> BoxStream<'static, ReadBatchTask> {
1580 let stream = futures::stream::unfold(self, |mut slf| async move {
1581 let next_task = match slf.next_batch_task().await {
1582 Ok(Some(next_task)) => next_task,
1583 Ok(None) => return None,
1584 Err(err) => {
1585 slf.rows_remaining = 0;
1586 return Some((
1587 ReadBatchTask {
1588 task: async move { Err(err) }.boxed(),
1589 num_rows: 0,
1590 },
1591 slf,
1592 ));
1593 }
1594 };
1595 let num_rows = next_task.num_rows;
1596 let emitted_batch_size_warning = slf.emitted_batch_size_warning.clone();
1597 let task = async move {
1598 let (batch, _data_size) =
1602 tokio::spawn(async move { next_task.into_batch(emitted_batch_size_warning) })
1603 .await
1604 .map_err(|err| Error::wrapped(err.into()))??;
1605 Ok(batch)
1606 };
1607 debug_assert!(num_rows <= u32::MAX as u64);
1609 Some((
1610 ReadBatchTask {
1611 task: task.boxed(),
1612 num_rows: num_rows as u32,
1613 },
1614 slf,
1615 ))
1616 });
1617 stream.boxed()
1618 }
1619}
1620
1621enum RootDecoderMessage {
1624 LoadedPage(LoadedPageShard),
1625 ArrayPage(DecoderReady),
1626}
1627trait RootDecoderType {
1628 fn accept_message(&mut self, message: RootDecoderMessage) -> Result<()>;
1629 fn drain_batch(&mut self, num_rows: u64) -> Result<NextDecodeTask>;
1630 fn wait(&mut self, loaded_need: u64, runtime: &tokio::runtime::Runtime) -> Result<()>;
1631}
1632impl RootDecoderType for StructuralStructDecoder {
1633 fn accept_message(&mut self, message: RootDecoderMessage) -> Result<()> {
1634 let RootDecoderMessage::LoadedPage(loaded_page) = message else {
1635 unreachable!()
1636 };
1637 self.accept_page(loaded_page)
1638 }
1639 fn drain_batch(&mut self, num_rows: u64) -> Result<NextDecodeTask> {
1640 self.drain_batch_task(num_rows)
1641 }
1642 fn wait(&mut self, _: u64, _: &tokio::runtime::Runtime) -> Result<()> {
1643 Ok(())
1645 }
1646}
1647impl RootDecoderType for SimpleStructDecoder {
1648 fn accept_message(&mut self, message: RootDecoderMessage) -> Result<()> {
1649 let RootDecoderMessage::ArrayPage(array_page) = message else {
1650 unreachable!()
1651 };
1652 self.accept_child(array_page)
1653 }
1654 fn drain_batch(&mut self, num_rows: u64) -> Result<NextDecodeTask> {
1655 self.drain(num_rows)
1656 }
1657 fn wait(&mut self, loaded_need: u64, runtime: &tokio::runtime::Runtime) -> Result<()> {
1658 runtime.block_on(self.wait_for_loaded(loaded_need))
1659 }
1660}
1661
1662struct BatchDecodeIterator<T: RootDecoderType> {
1664 messages: VecDeque<Result<DecoderMessage>>,
1665 root_decoder: T,
1666 rows_remaining: u64,
1667 rows_per_batch: u32,
1668 rows_scheduled: u64,
1669 rows_drained: u64,
1670 emitted_batch_size_warning: Arc<Once>,
1671 wait_for_io_runtime: tokio::runtime::Runtime,
1675 schema: Arc<ArrowSchema>,
1676}
1677
1678impl<T: RootDecoderType> BatchDecodeIterator<T> {
1679 pub fn new(
1681 messages: VecDeque<Result<DecoderMessage>>,
1682 rows_per_batch: u32,
1683 num_rows: u64,
1684 root_decoder: T,
1685 schema: Arc<ArrowSchema>,
1686 ) -> Self {
1687 Self {
1688 messages,
1689 root_decoder,
1690 rows_remaining: num_rows,
1691 rows_per_batch,
1692 rows_scheduled: 0,
1693 rows_drained: 0,
1694 wait_for_io_runtime: tokio::runtime::Builder::new_current_thread()
1695 .build()
1696 .unwrap(),
1697 emitted_batch_size_warning: Arc::new(Once::new()),
1698 schema,
1699 }
1700 }
1701
1702 fn wait_for_page(&self, unloaded_page: UnloadedPageShard) -> Result<LoadedPageShard> {
1707 match maybe_done(unloaded_page.0) {
1708 MaybeDone::Done(loaded_page) => loaded_page,
1710 MaybeDone::Future(fut) => self.wait_for_io_runtime.block_on(fut),
1712 MaybeDone::Gone => unreachable!(),
1713 }
1714 }
1715
1716 #[instrument(level = "debug", skip_all)]
1721 fn wait_for_io(&mut self, scheduled_need: u64, to_take: u64) -> Result<u64> {
1722 while self.rows_scheduled < scheduled_need && !self.messages.is_empty() {
1723 let message = self.messages.pop_front().unwrap()?;
1724 self.rows_scheduled = message.scheduled_so_far;
1725 for decoder_message in message.decoders {
1726 match decoder_message {
1727 MessageType::UnloadedPage(unloaded_page) => {
1728 let loaded_page = self.wait_for_page(unloaded_page)?;
1729 self.root_decoder
1730 .accept_message(RootDecoderMessage::LoadedPage(loaded_page))?;
1731 }
1732 MessageType::DecoderReady(decoder_ready) => {
1733 if !decoder_ready.path.is_empty() {
1735 self.root_decoder
1736 .accept_message(RootDecoderMessage::ArrayPage(decoder_ready))?;
1737 }
1738 }
1739 }
1740 }
1741 }
1742
1743 let loaded_need = self.rows_drained + to_take.min(self.rows_per_batch as u64) - 1;
1744
1745 self.root_decoder
1746 .wait(loaded_need, &self.wait_for_io_runtime)?;
1747 Ok(self.rows_scheduled)
1748 }
1749
1750 #[instrument(level = "debug", skip_all)]
1751 fn next_batch_task(&mut self) -> Result<Option<RecordBatch>> {
1752 trace!(
1753 "Draining batch task (rows_remaining={} rows_drained={} rows_scheduled={})",
1754 self.rows_remaining, self.rows_drained, self.rows_scheduled,
1755 );
1756 if self.rows_remaining == 0 {
1757 return Ok(None);
1758 }
1759
1760 let mut to_take = self.rows_remaining.min(self.rows_per_batch as u64);
1761 self.rows_remaining -= to_take;
1762
1763 let scheduled_need = (self.rows_drained + to_take).saturating_sub(self.rows_scheduled);
1764 trace!(
1765 "scheduled_need = {} because rows_drained = {} and to_take = {} and rows_scheduled = {}",
1766 scheduled_need, self.rows_drained, to_take, self.rows_scheduled
1767 );
1768 if scheduled_need > 0 {
1769 let desired_scheduled = scheduled_need + self.rows_scheduled;
1770 trace!(
1771 "Draining from scheduler (desire at least {} scheduled rows)",
1772 desired_scheduled
1773 );
1774 let actually_scheduled = self.wait_for_io(desired_scheduled, to_take)?;
1775 if actually_scheduled < desired_scheduled {
1776 let under_scheduled = desired_scheduled - actually_scheduled;
1777 to_take -= under_scheduled;
1778 }
1779 }
1780
1781 if to_take == 0 {
1782 return Ok(None);
1783 }
1784
1785 let next_task = self.root_decoder.drain_batch(to_take)?;
1786
1787 self.rows_drained += to_take;
1788
1789 let (batch, _data_size) = next_task.into_batch(self.emitted_batch_size_warning.clone())?;
1790
1791 Ok(Some(batch))
1792 }
1793}
1794
1795impl<T: RootDecoderType> Iterator for BatchDecodeIterator<T> {
1796 type Item = ArrowResult<RecordBatch>;
1797
1798 fn next(&mut self) -> Option<Self::Item> {
1799 self.next_batch_task()
1800 .transpose()
1801 .map(|r| r.map_err(ArrowError::from))
1802 }
1803}
1804
1805impl<T: RootDecoderType> RecordBatchReader for BatchDecodeIterator<T> {
1806 fn schema(&self) -> Arc<ArrowSchema> {
1807 self.schema.clone()
1808 }
1809}
1810
1811pub fn estimate_bytes_per_row(data_type: &DataType) -> f64 {
1827 if let Some(w) = data_type.byte_width_opt() {
1828 return w as f64;
1829 }
1830 match data_type {
1831 DataType::Boolean => 1.0 / 8.0,
1832 DataType::Utf8 | DataType::Binary | DataType::LargeUtf8 | DataType::LargeBinary => 64.0,
1833 DataType::Struct(fields) => fields
1834 .iter()
1835 .map(|f| estimate_bytes_per_row(f.data_type()))
1836 .sum(),
1837 DataType::List(child) | DataType::LargeList(child) => {
1838 5.0 * estimate_bytes_per_row(child.data_type())
1839 }
1840 DataType::FixedSizeList(child, dim) => {
1841 *dim as f64 * estimate_bytes_per_row(child.data_type())
1842 }
1843 DataType::Dictionary(_, value_type) => estimate_bytes_per_row(value_type),
1844 DataType::Map(entries, _) => 5.0 * estimate_bytes_per_row(entries.data_type()),
1845 _ => 64.0,
1846 }
1847}
1848
1849pub struct StructuralBatchDecodeStream {
1851 context: DecoderContext,
1852 root_decoder: StructuralStructDecoder,
1853 rows_remaining: u64,
1854 rows_per_batch: u32,
1855 rows_scheduled: u64,
1856 rows_drained: u64,
1857 scheduler_exhausted: bool,
1858 emitted_batch_size_warning: Arc<Once>,
1859 spawn_batch_decode_tasks: bool,
1867 batch_size_bytes: Option<u64>,
1870 schema_bytes_per_row: f64,
1873 bytes_per_row_feedback: Arc<AtomicU64>,
1876}
1877
1878impl StructuralBatchDecodeStream {
1879 pub fn new(
1889 scheduled: mpsc::UnboundedReceiver<Result<DecoderMessage>>,
1890 rows_per_batch: u32,
1891 num_rows: u64,
1892 root_decoder: StructuralStructDecoder,
1893 spawn_batch_decode_tasks: bool,
1894 batch_size_bytes: Option<u64>,
1895 ) -> Self {
1896 let schema_bytes_per_row = if batch_size_bytes.is_some() {
1897 estimate_bytes_per_row(root_decoder.data_type()).max(1.0)
1898 } else {
1899 0.0
1900 };
1901 Self {
1902 context: DecoderContext::new(scheduled),
1903 root_decoder,
1904 rows_remaining: num_rows,
1905 rows_per_batch,
1906 rows_scheduled: 0,
1907 rows_drained: 0,
1908 scheduler_exhausted: false,
1909 emitted_batch_size_warning: Arc::new(Once::new()),
1910 spawn_batch_decode_tasks,
1911 batch_size_bytes,
1912 schema_bytes_per_row,
1913 bytes_per_row_feedback: Arc::new(AtomicU64::new(0)),
1914 }
1915 }
1916
1917 #[instrument(level = "debug", skip_all)]
1918 async fn wait_for_scheduled(&mut self, scheduled_need: u64) -> Result<u64> {
1919 if self.scheduler_exhausted {
1920 return Ok(self.rows_scheduled);
1921 }
1922 while self.rows_scheduled < scheduled_need {
1923 let next_message = self.context.source.recv().await;
1924 match next_message {
1925 Some(scan_line) => {
1926 let scan_line = scan_line?;
1927 self.rows_scheduled = scan_line.scheduled_so_far;
1928 for message in scan_line.decoders {
1929 let unloaded_page = message.into_structural();
1930 let loaded_page = unloaded_page.0.await?;
1931 self.root_decoder.accept_page(loaded_page)?;
1932 }
1933 }
1934 None => {
1935 self.scheduler_exhausted = true;
1939 return Ok(self.rows_scheduled);
1940 }
1941 }
1942 }
1943 Ok(scheduled_need)
1944 }
1945
1946 #[instrument(level = "debug", skip_all)]
1947 async fn next_batch_task(&mut self) -> Result<Option<NextDecodeTask>> {
1948 trace!(
1949 "Draining batch task (rows_remaining={} rows_drained={} rows_scheduled={})",
1950 self.rows_remaining, self.rows_drained, self.rows_scheduled,
1951 );
1952 if self.rows_remaining == 0 {
1953 return Ok(None);
1954 }
1955
1956 let row_limit = self.rows_remaining.min(self.rows_per_batch as u64);
1957 let mut to_take = if let Some(batch_size_bytes) = self.batch_size_bytes {
1958 let feedback = self.bytes_per_row_feedback.load(Ordering::Relaxed);
1959 let bpr = if feedback > 0 {
1960 feedback as f64
1961 } else {
1962 self.schema_bytes_per_row
1963 };
1964 let rows = (batch_size_bytes as f64 / bpr) as u64;
1965 row_limit.min(rows.max(1))
1966 } else {
1967 row_limit
1968 };
1969 self.rows_remaining -= to_take;
1970
1971 let scheduled_need = (self.rows_drained + to_take).saturating_sub(self.rows_scheduled);
1972 trace!(
1973 "scheduled_need = {} because rows_drained = {} and to_take = {} and rows_scheduled = {}",
1974 scheduled_need, self.rows_drained, to_take, self.rows_scheduled
1975 );
1976 if scheduled_need > 0 {
1977 let desired_scheduled = scheduled_need + self.rows_scheduled;
1978 trace!(
1979 "Draining from scheduler (desire at least {} scheduled rows)",
1980 desired_scheduled
1981 );
1982 let actually_scheduled = self.wait_for_scheduled(desired_scheduled).await?;
1983 if actually_scheduled < desired_scheduled {
1984 let under_scheduled = desired_scheduled - actually_scheduled;
1985 to_take -= under_scheduled;
1986 }
1987 }
1988
1989 if to_take == 0 {
1990 return Ok(None);
1991 }
1992
1993 let next_task = self.root_decoder.drain_batch_task(to_take)?;
1994 self.rows_drained += to_take;
1995 Ok(Some(next_task))
1996 }
1997
1998 pub fn into_stream(self) -> BoxStream<'static, ReadBatchTask> {
1999 let stream = futures::stream::unfold(self, |mut slf| async move {
2000 let next_task = match slf.next_batch_task().await {
2001 Ok(Some(next_task)) => next_task,
2002 Ok(None) => return None,
2003 Err(err) => {
2004 slf.rows_remaining = 0;
2005 return Some((
2006 ReadBatchTask {
2007 task: async move { Err(err) }.boxed(),
2008 num_rows: 0,
2009 },
2010 slf,
2011 ));
2012 }
2013 };
2014 let num_rows = next_task.num_rows;
2015 let emitted_batch_size_warning = slf.emitted_batch_size_warning.clone();
2016 let bytes_per_row_feedback = slf.bytes_per_row_feedback.clone();
2017 let spawn_batch_decode_tasks = slf.spawn_batch_decode_tasks;
2020 let task = async move {
2021 let (batch, data_size) = if spawn_batch_decode_tasks {
2022 tokio::spawn(async move { next_task.into_batch(emitted_batch_size_warning) })
2023 .await
2024 .map_err(|err| Error::wrapped(err.into()))??
2025 } else {
2026 next_task.into_batch(emitted_batch_size_warning)?
2027 };
2028 let num_rows = batch.num_rows() as u64;
2029 if let Some(bpr) = data_size.checked_div(num_rows) {
2030 let prev = bytes_per_row_feedback.load(Ordering::Relaxed);
2031 let next = if prev == 0 || bpr >= prev {
2032 bpr
2035 } else {
2036 (prev + bpr) / 2
2040 };
2041 bytes_per_row_feedback.store(next.max(1), Ordering::Relaxed);
2042 }
2043 Ok(batch)
2044 };
2045 debug_assert!(num_rows <= u32::MAX as u64);
2047 Some((
2048 ReadBatchTask {
2049 task: task.boxed(),
2050 num_rows: num_rows as u32,
2051 },
2052 slf,
2053 ))
2054 });
2055 stream.boxed()
2056 }
2057}
2058
2059#[derive(Debug)]
2060pub enum RequestedRows {
2061 Ranges(Vec<Range<u64>>),
2062 Indices(Vec<u64>),
2063}
2064
2065impl RequestedRows {
2066 pub fn num_rows(&self) -> u64 {
2067 match self {
2068 Self::Ranges(ranges) => ranges.iter().map(|r| r.end - r.start).sum(),
2069 Self::Indices(indices) => indices.len() as u64,
2070 }
2071 }
2072
2073 pub fn trim_empty_ranges(mut self) -> Self {
2074 if let Self::Ranges(ranges) = &mut self {
2075 ranges.retain(|r| !r.is_empty());
2076 }
2077 self
2078 }
2079}
2080
2081#[derive(Debug, Clone)]
2083pub struct DecoderConfig {
2084 pub cache_repetition_index: bool,
2091 pub validate_on_decode: bool,
2093 pub inline_scheduling: Option<bool>,
2111}
2112
2113impl Default for DecoderConfig {
2114 fn default() -> Self {
2115 Self {
2116 cache_repetition_index: default_cache_repetition_index(),
2117 validate_on_decode: false,
2118 inline_scheduling: None,
2119 }
2120 }
2121}
2122
2123#[derive(Debug, Clone)]
2124pub struct SchedulerDecoderConfig {
2125 pub decoder_plugins: Arc<DecoderPlugins>,
2126 pub batch_size: u32,
2127 pub io: Arc<dyn EncodingsIo>,
2128 pub cache: Arc<LanceCache>,
2129 pub decoder_config: DecoderConfig,
2131 pub batch_size_bytes: Option<u64>,
2137}
2138
2139fn check_scheduler_on_drop(
2140 stream: BoxStream<'static, ReadBatchTask>,
2141 scheduler_handle: tokio::task::JoinHandle<()>,
2142) -> BoxStream<'static, ReadBatchTask> {
2143 let abort_handle = scheduler_handle.abort_handle();
2147 let mut scheduler_handle = Some(scheduler_handle);
2148 let check_scheduler = stream::unfold((), move |_| {
2149 let handle = scheduler_handle.take();
2150 async move {
2151 if let Some(handle) = handle {
2152 handle.await.unwrap();
2153 }
2154 None
2155 }
2156 });
2157 stream
2158 .chain(check_scheduler)
2159 .on_drop(move || {
2160 abort_handle.abort();
2168 })
2169 .boxed()
2170}
2171
2172#[allow(clippy::too_many_arguments)]
2173pub fn create_decode_stream(
2174 schema: &Schema,
2175 num_rows: u64,
2176 batch_size: u32,
2177 is_structural: bool,
2178 should_validate: bool,
2179 spawn_structural_batch_decode_tasks: bool,
2180 rx: mpsc::UnboundedReceiver<Result<DecoderMessage>>,
2181 batch_size_bytes: Option<u64>,
2182) -> Result<BoxStream<'static, ReadBatchTask>> {
2183 if is_structural {
2184 let arrow_schema = ArrowSchema::from(schema);
2185 let structural_decoder = StructuralStructDecoder::new(
2186 arrow_schema.fields,
2187 should_validate,
2188 true,
2189 )?;
2190 Ok(StructuralBatchDecodeStream::new(
2191 rx,
2192 batch_size,
2193 num_rows,
2194 structural_decoder,
2195 spawn_structural_batch_decode_tasks,
2196 batch_size_bytes,
2197 )
2198 .into_stream())
2199 } else {
2200 if batch_size_bytes.is_some() {
2201 warn!("batch_size_bytes is not supported for v2.0 files and will be ignored");
2202 }
2203 let arrow_schema = ArrowSchema::from(schema);
2204 let root_fields = arrow_schema.fields;
2205
2206 let simple_struct_decoder = SimpleStructDecoder::new(root_fields, num_rows);
2207 Ok(BatchDecodeStream::new(rx, batch_size, num_rows, simple_struct_decoder).into_stream())
2208 }
2209}
2210
2211pub fn create_decode_iterator(
2215 schema: &Schema,
2216 num_rows: u64,
2217 batch_size: u32,
2218 should_validate: bool,
2219 is_structural: bool,
2220 messages: VecDeque<Result<DecoderMessage>>,
2221) -> Result<Box<dyn RecordBatchReader + Send + 'static>> {
2222 let arrow_schema = Arc::new(ArrowSchema::from(schema));
2223 let root_fields = arrow_schema.fields.clone();
2224 if is_structural {
2225 let simple_struct_decoder =
2226 StructuralStructDecoder::new(root_fields, should_validate, true)?;
2227 Ok(Box::new(BatchDecodeIterator::new(
2228 messages,
2229 batch_size,
2230 num_rows,
2231 simple_struct_decoder,
2232 arrow_schema,
2233 )))
2234 } else {
2235 let root_decoder = SimpleStructDecoder::new(root_fields, num_rows);
2236 Ok(Box::new(BatchDecodeIterator::new(
2237 messages,
2238 batch_size,
2239 num_rows,
2240 root_decoder,
2241 arrow_schema,
2242 )))
2243 }
2244}
2245
2246async fn create_scheduler_decoder(
2247 column_infos: Vec<Arc<ColumnInfo>>,
2248 requested_rows: RequestedRows,
2249 filter: FilterExpression,
2250 column_indices: Vec<u32>,
2251 target_schema: Arc<Schema>,
2252 config: SchedulerDecoderConfig,
2253) -> Result<BoxStream<'static, ReadBatchTask>> {
2254 let num_rows = requested_rows.num_rows();
2255
2256 let is_structural = column_infos[0].is_structural();
2257 let mode = std::env::var(ENV_LANCE_STRUCTURAL_BATCH_DECODE_SPAWN_MODE);
2258 let spawn_structural_batch_decode_tasks = match mode.ok().as_deref() {
2259 Some("always") => true,
2260 Some("never") => false,
2261 _ => matches!(requested_rows, RequestedRows::Ranges(_)),
2262 };
2263
2264 let (tx, rx) = mpsc::unbounded_channel();
2265
2266 let decode_stream = create_decode_stream(
2267 &target_schema,
2268 num_rows,
2269 config.batch_size,
2270 is_structural,
2271 config.decoder_config.validate_on_decode,
2272 spawn_structural_batch_decode_tasks,
2273 rx,
2274 config.batch_size_bytes,
2275 )?;
2276
2277 let mut decode_scheduler = DecodeBatchScheduler::try_new(
2282 target_schema.as_ref(),
2283 &column_indices,
2284 &column_infos,
2285 &vec![],
2286 num_rows,
2287 config.decoder_plugins,
2288 config.io.clone(),
2289 config.cache,
2290 &filter,
2291 &config.decoder_config,
2292 )
2293 .await?;
2294
2295 let inline_scheduling = config
2301 .decoder_config
2302 .inline_scheduling
2303 .unwrap_or_else(|| num_rows <= inline_scheduling_threshold());
2304
2305 if inline_scheduling {
2306 match requested_rows {
2307 RequestedRows::Ranges(ranges) => {
2308 decode_scheduler.schedule_ranges(&ranges, &filter, tx, config.io)
2309 }
2310 RequestedRows::Indices(indices) => {
2311 decode_scheduler.schedule_take(&indices, &filter, tx, config.io)
2312 }
2313 }
2314 Ok(decode_stream)
2315 } else {
2316 let scheduling = async move {
2320 match requested_rows {
2321 RequestedRows::Ranges(ranges) => {
2322 decode_scheduler.schedule_ranges(&ranges, &filter, tx, config.io)
2323 }
2324 RequestedRows::Indices(indices) => {
2325 decode_scheduler.schedule_take(&indices, &filter, tx, config.io)
2326 }
2327 }
2328 };
2329 let scheduler_handle = tokio::task::spawn(scheduling);
2330 Ok(check_scheduler_on_drop(decode_stream, scheduler_handle))
2331 }
2332}
2333
2334pub async fn schedule_and_decode(
2349 column_infos: Vec<Arc<ColumnInfo>>,
2350 requested_rows: RequestedRows,
2351 filter: FilterExpression,
2352 column_indices: Vec<u32>,
2353 target_schema: Arc<Schema>,
2354 config: SchedulerDecoderConfig,
2355) -> Result<BoxStream<'static, ReadBatchTask>> {
2356 if requested_rows.num_rows() == 0 {
2357 return Ok(stream::empty().boxed());
2358 }
2359
2360 let requested_rows = requested_rows.trim_empty_ranges();
2363
2364 let io = config.io.clone();
2365
2366 let stream = create_scheduler_decoder(
2367 column_infos,
2368 requested_rows,
2369 filter,
2370 column_indices,
2371 target_schema,
2372 config,
2373 )
2374 .await?;
2375
2376 Ok(stream.finally(move || drop(io)).boxed())
2379}
2380
2381pub static WAITER_RT: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
2382 tokio::runtime::Builder::new_current_thread()
2383 .build()
2384 .unwrap()
2385});
2386
2387pub fn schedule_and_decode_blocking(
2402 column_infos: Vec<Arc<ColumnInfo>>,
2403 requested_rows: RequestedRows,
2404 filter: FilterExpression,
2405 column_indices: Vec<u32>,
2406 target_schema: Arc<Schema>,
2407 config: SchedulerDecoderConfig,
2408) -> Result<Box<dyn RecordBatchReader + Send + 'static>> {
2409 if requested_rows.num_rows() == 0 {
2410 let arrow_schema = Arc::new(ArrowSchema::from(target_schema.as_ref()));
2411 return Ok(Box::new(RecordBatchIterator::new(vec![], arrow_schema)));
2412 }
2413
2414 let num_rows = requested_rows.num_rows();
2415 let is_structural = column_infos[0].is_structural();
2416
2417 let (tx, mut rx) = mpsc::unbounded_channel();
2418
2419 let mut decode_scheduler = WAITER_RT.block_on(DecodeBatchScheduler::try_new(
2422 target_schema.as_ref(),
2423 &column_indices,
2424 &column_infos,
2425 &vec![],
2426 num_rows,
2427 config.decoder_plugins,
2428 config.io.clone(),
2429 config.cache,
2430 &filter,
2431 &config.decoder_config,
2432 ))?;
2433
2434 match requested_rows {
2436 RequestedRows::Ranges(ranges) => {
2437 decode_scheduler.schedule_ranges(&ranges, &filter, tx, config.io)
2438 }
2439 RequestedRows::Indices(indices) => {
2440 decode_scheduler.schedule_take(&indices, &filter, tx, config.io)
2441 }
2442 }
2443
2444 let mut messages = Vec::new();
2446 while rx
2447 .recv_many(&mut messages, usize::MAX)
2448 .now_or_never()
2449 .unwrap()
2450 != 0
2451 {}
2452
2453 let decode_iterator = create_decode_iterator(
2455 &target_schema,
2456 num_rows,
2457 config.batch_size,
2458 config.decoder_config.validate_on_decode,
2459 is_structural,
2460 messages.into(),
2461 )?;
2462
2463 Ok(decode_iterator)
2464}
2465
2466pub trait PrimitivePageDecoder: Send + Sync {
2478 fn decode(&self, rows_to_skip: u64, num_rows: u64) -> Result<DataBlock>;
2510}
2511
2512pub trait PageScheduler: Send + Sync + std::fmt::Debug {
2521 fn schedule_ranges(
2533 &self,
2534 ranges: &[Range<u64>],
2535 scheduler: &Arc<dyn EncodingsIo>,
2536 top_level_row: u64,
2537 ) -> BoxFuture<'static, Result<Box<dyn PrimitivePageDecoder>>>;
2538}
2539
2540pub trait PriorityRange: std::fmt::Debug + Send + Sync {
2542 fn advance(&mut self, num_rows: u64);
2543 fn current_priority(&self) -> u64;
2544 fn box_clone(&self) -> Box<dyn PriorityRange>;
2545}
2546
2547#[derive(Debug)]
2550pub struct SimplePriorityRange {
2551 priority: u64,
2552}
2553
2554impl SimplePriorityRange {
2555 fn new(priority: u64) -> Self {
2556 Self { priority }
2557 }
2558}
2559
2560impl PriorityRange for SimplePriorityRange {
2561 fn advance(&mut self, num_rows: u64) {
2562 self.priority += num_rows;
2563 }
2564
2565 fn current_priority(&self) -> u64 {
2566 self.priority
2567 }
2568
2569 fn box_clone(&self) -> Box<dyn PriorityRange> {
2570 Box::new(Self {
2571 priority: self.priority,
2572 })
2573 }
2574}
2575
2576pub struct ListPriorityRange {
2589 base: Box<dyn PriorityRange>,
2590 offsets: Arc<[u64]>,
2591 cur_index_into_offsets: usize,
2592 cur_position: u64,
2593}
2594
2595impl ListPriorityRange {
2596 pub(crate) fn new(base: Box<dyn PriorityRange>, offsets: Arc<[u64]>) -> Self {
2597 Self {
2598 base,
2599 offsets,
2600 cur_index_into_offsets: 0,
2601 cur_position: 0,
2602 }
2603 }
2604}
2605
2606impl std::fmt::Debug for ListPriorityRange {
2607 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2608 f.debug_struct("ListPriorityRange")
2609 .field("base", &self.base)
2610 .field("offsets.len()", &self.offsets.len())
2611 .field("cur_index_into_offsets", &self.cur_index_into_offsets)
2612 .field("cur_position", &self.cur_position)
2613 .finish()
2614 }
2615}
2616
2617impl PriorityRange for ListPriorityRange {
2618 fn advance(&mut self, num_rows: u64) {
2619 self.cur_position += num_rows;
2622 let mut idx_into_offsets = self.cur_index_into_offsets;
2623 while idx_into_offsets + 1 < self.offsets.len()
2624 && self.offsets[idx_into_offsets + 1] <= self.cur_position
2625 {
2626 idx_into_offsets += 1;
2627 }
2628 let base_rows_advanced = idx_into_offsets - self.cur_index_into_offsets;
2629 self.cur_index_into_offsets = idx_into_offsets;
2630 self.base.advance(base_rows_advanced as u64);
2631 }
2632
2633 fn current_priority(&self) -> u64 {
2634 self.base.current_priority()
2635 }
2636
2637 fn box_clone(&self) -> Box<dyn PriorityRange> {
2638 Box::new(Self {
2639 base: self.base.box_clone(),
2640 offsets: self.offsets.clone(),
2641 cur_index_into_offsets: self.cur_index_into_offsets,
2642 cur_position: self.cur_position,
2643 })
2644 }
2645}
2646
2647pub struct SchedulerContext {
2649 recv: Option<mpsc::UnboundedReceiver<DecoderMessage>>,
2650 io: Arc<dyn EncodingsIo>,
2651 cache: Arc<LanceCache>,
2652 name: String,
2653 path: Vec<u32>,
2654 path_names: Vec<String>,
2655}
2656
2657pub struct ScopedSchedulerContext<'a> {
2658 pub context: &'a mut SchedulerContext,
2659}
2660
2661impl<'a> ScopedSchedulerContext<'a> {
2662 pub fn pop(self) -> &'a mut SchedulerContext {
2663 self.context.pop();
2664 self.context
2665 }
2666}
2667
2668impl SchedulerContext {
2669 pub fn new(io: Arc<dyn EncodingsIo>, cache: Arc<LanceCache>) -> Self {
2670 Self {
2671 io,
2672 cache,
2673 recv: None,
2674 name: "".to_string(),
2675 path: Vec::new(),
2676 path_names: Vec::new(),
2677 }
2678 }
2679
2680 pub fn io(&self) -> &Arc<dyn EncodingsIo> {
2681 &self.io
2682 }
2683
2684 pub fn cache(&self) -> &Arc<LanceCache> {
2685 &self.cache
2686 }
2687
2688 pub fn push(&'_ mut self, name: &str, index: u32) -> ScopedSchedulerContext<'_> {
2689 self.path.push(index);
2690 self.path_names.push(name.to_string());
2691 ScopedSchedulerContext { context: self }
2692 }
2693
2694 pub fn pop(&mut self) {
2695 self.path.pop();
2696 self.path_names.pop();
2697 }
2698
2699 pub fn path_name(&self) -> String {
2700 let path = self.path_names.join("/");
2701 if self.recv.is_some() {
2702 format!("TEMP({}){}", self.name, path)
2703 } else {
2704 format!("ROOT{}", path)
2705 }
2706 }
2707
2708 pub fn current_path(&self) -> VecDeque<u32> {
2709 VecDeque::from_iter(self.path.iter().copied())
2710 }
2711
2712 #[deprecated(since = "0.29.1", note = "This is for v2.0 array-encoding paths")]
2713 pub fn locate_decoder(&mut self, decoder: Box<dyn LogicalPageDecoder>) -> DecoderReady {
2714 trace!(
2715 "Scheduling decoder of type {:?} for {:?}",
2716 decoder.data_type(),
2717 self.path,
2718 );
2719 DecoderReady {
2720 decoder,
2721 path: self.current_path(),
2722 }
2723 }
2724}
2725
2726pub struct UnloadedPageShard(pub BoxFuture<'static, Result<LoadedPageShard>>);
2727
2728impl std::fmt::Debug for UnloadedPageShard {
2729 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2730 f.debug_struct("UnloadedPage").finish()
2731 }
2732}
2733
2734#[derive(Debug)]
2735pub struct ScheduledScanLine {
2736 pub rows_scheduled: u64,
2737 pub decoders: Vec<MessageType>,
2738}
2739
2740pub trait StructuralSchedulingJob: std::fmt::Debug {
2741 fn schedule_next(&mut self, context: &mut SchedulerContext) -> Result<Vec<ScheduledScanLine>>;
2748}
2749
2750pub struct FilterExpression(pub Bytes);
2758
2759impl FilterExpression {
2760 pub fn no_filter() -> Self {
2765 Self(Bytes::new())
2766 }
2767
2768 pub fn is_noop(&self) -> bool {
2770 self.0.is_empty()
2771 }
2772}
2773
2774pub trait StructuralFieldScheduler: Send + std::fmt::Debug {
2775 fn initialize<'a>(
2776 &'a mut self,
2777 filter: &'a FilterExpression,
2778 context: &'a SchedulerContext,
2779 ) -> BoxFuture<'a, Result<()>>;
2780 fn schedule_ranges<'a>(
2781 &'a self,
2782 ranges: &[Range<u64>],
2783 filter: &FilterExpression,
2784 ) -> Result<Box<dyn StructuralSchedulingJob + 'a>>;
2785}
2786
2787pub trait DecodeArrayTask: Send {
2789 fn decode(self: Box<Self>) -> Result<(ArrayRef, u64)>;
2791}
2792
2793impl DecodeArrayTask for Box<dyn StructuralDecodeArrayTask> {
2794 fn decode(self: Box<Self>) -> Result<(ArrayRef, u64)> {
2795 let decoded_array = StructuralDecodeArrayTask::decode(*self)?;
2796 decoded_array.repdef.ensure_exhausted()?;
2797 Ok((decoded_array.array, decoded_array.data_size))
2798 }
2799}
2800
2801pub struct NextDecodeTask {
2806 pub task: Box<dyn DecodeArrayTask>,
2808 pub num_rows: u64,
2810}
2811
2812impl NextDecodeTask {
2813 #[instrument(name = "task_to_batch", level = "debug", skip_all)]
2818 fn into_batch(self, emitted_batch_size_warning: Arc<Once>) -> Result<(RecordBatch, u64)> {
2819 let (struct_arr, data_size) = self.task.decode()?;
2820 let batch = RecordBatch::from(struct_arr.as_struct());
2821 if data_size > BATCH_SIZE_BYTES_WARNING {
2822 emitted_batch_size_warning.call_once(|| {
2823 let size_mb = data_size / 1024 / 1024;
2824 debug!("Lance read in a single batch that contained more than {}MiB of data. You may want to consider reducing the batch size.", size_mb);
2825 });
2826 }
2827 Ok((batch, data_size))
2828 }
2829}
2830
2831#[derive(Debug)]
2835pub enum MessageType {
2836 DecoderReady(DecoderReady),
2841 UnloadedPage(UnloadedPageShard),
2845}
2846
2847impl MessageType {
2848 pub fn into_array(self) -> DecoderReady {
2849 match self {
2850 Self::DecoderReady(decoder) => decoder,
2851 Self::UnloadedPage(_) => {
2852 panic!("Expected DecoderReady but got UnloadedPage")
2853 }
2854 }
2855 }
2856
2857 pub fn into_structural(self) -> UnloadedPageShard {
2858 match self {
2859 Self::UnloadedPage(unloaded) => unloaded,
2860 Self::DecoderReady(_) => {
2861 panic!("Expected UnloadedPage but got DecoderReady")
2862 }
2863 }
2864 }
2865}
2866
2867pub struct DecoderMessage {
2868 pub scheduled_so_far: u64,
2869 pub decoders: Vec<MessageType>,
2870}
2871
2872pub struct DecoderContext {
2873 source: mpsc::UnboundedReceiver<Result<DecoderMessage>>,
2874}
2875
2876impl DecoderContext {
2877 pub fn new(source: mpsc::UnboundedReceiver<Result<DecoderMessage>>) -> Self {
2878 Self { source }
2879 }
2880}
2881
2882pub struct DecodedPage {
2883 pub data: DataBlock,
2884 pub repdef: RepDefUnraveler,
2885}
2886
2887pub trait DecodePageTask: Send + std::fmt::Debug {
2888 fn decode(self: Box<Self>) -> Result<DecodedPage>;
2890}
2891
2892pub trait StructuralPageDecoder: std::fmt::Debug + Send {
2893 fn drain(&mut self, num_rows: u64) -> Result<Box<dyn DecodePageTask>>;
2894 fn num_rows(&self) -> u64;
2895 fn decoded_bytes(&self, _num_rows: u64) -> Result<u64> {
2898 Err(Error::not_supported(
2899 "decoded_bytes is not implemented for this page decoder".to_string(),
2900 ))
2901 }
2902}
2903
2904#[derive(Debug)]
2905pub struct LoadedPageShard {
2906 pub decoder: Box<dyn StructuralPageDecoder>,
2908 pub path: VecDeque<u32>,
2927}
2928
2929pub struct DecodedArray {
2930 pub array: ArrayRef,
2931 pub repdef: CompositeRepDefUnraveler,
2932 pub data_size: u64,
2934}
2935
2936pub trait StructuralDecodeArrayTask: std::fmt::Debug + Send {
2937 fn decode(self: Box<Self>) -> Result<DecodedArray>;
2938}
2939
2940pub trait StructuralFieldDecoder: std::fmt::Debug + Send {
2941 fn accept_page(&mut self, _child: LoadedPageShard) -> Result<()>;
2946 fn drain(&mut self, num_rows: u64) -> Result<Box<dyn StructuralDecodeArrayTask>>;
2948 fn data_type(&self) -> &DataType;
2950 fn plan_decoded_bytes(&self, _rows_remaining: u64) -> Result<[u64; 8]> {
2960 Err(Error::not_supported(
2961 "decoded_bytes is not implemented for this field decoder".to_string(),
2962 ))
2963 }
2964}
2965
2966#[derive(Debug, Default)]
2967pub struct DecoderPlugins {}
2968
2969#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2971pub enum EncodedBatchLayout {
2972 Array,
2974 Structural,
2976}
2977
2978pub async fn decode_batch(
2980 batch: &EncodedBatch,
2981 filter: &FilterExpression,
2982 decoder_plugins: Arc<DecoderPlugins>,
2983 should_validate: bool,
2984 layout: EncodedBatchLayout,
2985 cache: Option<Arc<LanceCache>>,
2986) -> Result<RecordBatch> {
2987 let io_scheduler = Arc::new(BufferScheduler::new(batch.data.clone())) as Arc<dyn EncodingsIo>;
2992 let cache = if let Some(cache) = cache {
2993 cache
2994 } else {
2995 Arc::new(lance_core::cache::LanceCache::with_capacity(
2996 128 * 1024 * 1024,
2997 ))
2998 };
2999 let mut decode_scheduler = DecodeBatchScheduler::try_new(
3000 batch.schema.as_ref(),
3001 &batch.top_level_columns,
3002 &batch.page_table,
3003 &vec![],
3004 batch.num_rows,
3005 decoder_plugins,
3006 io_scheduler.clone(),
3007 cache,
3008 filter,
3009 &DecoderConfig::default(),
3010 )
3011 .await?;
3012 let (tx, rx) = unbounded_channel();
3013 decode_scheduler.schedule_range(0..batch.num_rows, filter, tx, io_scheduler);
3014 let is_structural = layout == EncodedBatchLayout::Structural;
3015 let mode = std::env::var(ENV_LANCE_STRUCTURAL_BATCH_DECODE_SPAWN_MODE);
3016 let spawn_structural_batch_decode_tasks = !matches!(mode.ok().as_deref(), Some("never"));
3017 let mut decode_stream = create_decode_stream(
3018 &batch.schema,
3019 batch.num_rows,
3020 batch.num_rows as u32,
3021 is_structural,
3022 should_validate,
3023 spawn_structural_batch_decode_tasks,
3024 rx,
3025 None,
3026 )?;
3027 decode_stream.next().await.unwrap().task.await
3028}
3029
3030#[cfg(test)]
3031mod tests {
3033 use super::*;
3034 use std::collections::VecDeque;
3035
3036 #[derive(Debug)]
3037 struct FailingPageDecoder {
3038 page_data_type: DataType,
3039 total_rows: u64,
3040 load_error_message: &'static str,
3041 }
3042
3043 impl FailingPageDecoder {
3044 fn new(
3045 page_data_type: DataType,
3046 total_rows: u64,
3047 load_error_message: &'static str,
3048 ) -> Self {
3049 Self {
3050 page_data_type,
3051 total_rows,
3052 load_error_message,
3053 }
3054 }
3055 }
3056
3057 impl LogicalPageDecoder for FailingPageDecoder {
3058 fn wait_for_loaded(&'_ mut self, _rows_needed: u64) -> BoxFuture<'_, Result<()>> {
3059 let load_error_message = self.load_error_message;
3060 async move { Err(Error::io(load_error_message)) }.boxed()
3061 }
3062
3063 fn rows_loaded(&self) -> u64 {
3064 0
3065 }
3066
3067 fn num_rows(&self) -> u64 {
3068 self.total_rows
3069 }
3070
3071 fn rows_drained(&self) -> u64 {
3072 0
3073 }
3074
3075 fn drain(&mut self, requested_rows: u64) -> Result<NextDecodeTask> {
3076 Err(Error::internal(format!(
3077 "failing page decoder should not be drained after load error \
3078 (requested_rows={})",
3079 requested_rows
3080 )))
3081 }
3082
3083 fn data_type(&self) -> &DataType {
3084 &self.page_data_type
3085 }
3086 }
3087
3088 struct InvalidInputDecodeTask;
3089
3090 impl DecodeArrayTask for InvalidInputDecodeTask {
3091 fn decode(self: Box<Self>) -> Result<(ArrayRef, u64)> {
3092 Err(Error::invalid_input_source("malformed sparse page".into()))
3093 }
3094 }
3095
3096 #[test]
3097 fn next_decode_task_preserves_invalid_input_errors() {
3098 let err = NextDecodeTask {
3099 task: Box::new(InvalidInputDecodeTask),
3100 num_rows: 0,
3101 }
3102 .into_batch(Arc::new(Once::new()))
3103 .unwrap_err();
3104 assert!(matches!(err, Error::InvalidInput { .. }));
3105 }
3106
3107 #[test]
3108 fn test_read_zero_dimension_fsl_errors_instead_of_panicking() {
3109 use arrow_schema::Field as ArrowField;
3116
3117 let zero_dim = DataType::FixedSizeList(
3118 Arc::new(ArrowField::new("item", DataType::Float32, true)),
3119 0,
3120 );
3121 let field = Field::try_from(&ArrowField::new("vec", zero_dim, true)).unwrap();
3122 let strategy = CoreFieldDecoderStrategy::default();
3123
3124 let mut structural_columns = ColumnInfoIter::new(vec![], &[]);
3125 let err = strategy
3126 .create_structural_field_scheduler(&field, &mut structural_columns)
3127 .unwrap_err();
3128 assert!(
3129 err.to_string()
3130 .contains("dimension must be a positive integer"),
3131 "unexpected error: {}",
3132 err
3133 );
3134
3135 let mut array_columns = ColumnInfoIter::new(vec![], &[]);
3136 let err = strategy
3137 .create_array_field_scheduler(
3138 &field,
3139 &mut array_columns,
3140 FileBuffers {
3141 positions_and_sizes: &[],
3142 },
3143 )
3144 .unwrap_err();
3145 assert!(
3146 err.to_string()
3147 .contains("dimension must be a positive integer"),
3148 "unexpected error: {}",
3149 err
3150 );
3151 }
3152
3153 #[test]
3154 fn test_list_page_with_non_list_encoding_returns_error() {
3155 let item = Arc::new(ArrowField::new("item", DataType::Int32, true));
3156 let list = DataType::List(item);
3157 let field = Field::try_from(&ArrowField::new("values", list, true)).unwrap();
3158 let values_encoding = pb::ColumnEncoding {
3159 column_encoding: Some(pb::column_encoding::ColumnEncoding::Values(())),
3160 };
3161 let offsets_column = Arc::new(ColumnInfo::new(
3162 0,
3163 Arc::new([PageInfo {
3164 num_rows: 1,
3165 priority: 0,
3166 encoding: PageEncoding::Legacy(pb::ArrayEncoding {
3167 array_encoding: Some(pb::array_encoding::ArrayEncoding::Flat(
3168 pb::Flat::default(),
3169 )),
3170 }),
3171 buffer_offsets_and_sizes: Arc::new([]),
3172 }]),
3173 vec![],
3174 values_encoding.clone(),
3175 ));
3176 let items_column = Arc::new(ColumnInfo::new(1, Arc::new([]), vec![], values_encoding));
3177 let column_indices = [0, 1];
3178 let mut columns = ColumnInfoIter::new(vec![offsets_column, items_column], &column_indices);
3179
3180 let err = CoreFieldDecoderStrategy::default()
3181 .create_array_field_scheduler(
3182 &field,
3183 &mut columns,
3184 FileBuffers {
3185 positions_and_sizes: &[],
3186 },
3187 )
3188 .unwrap_err();
3189
3190 assert!(matches!(err, Error::InvalidInput { .. }));
3191 assert!(
3192 err.to_string()
3193 .contains("expected list encoding for field 'values' in column 0, page 0 but got"),
3194 "unexpected error: {err}"
3195 );
3196 }
3197
3198 #[tokio::test]
3199 async fn test_array_stream_stops_on_load_error() {
3200 use arrow_schema::Field as ArrowField;
3201
3202 let rows_per_batch = 1;
3203 let total_rows = 2;
3204 let scheduled_rows = 1;
3205 let page_rows = 1;
3206 let batch_readahead = 2;
3207 let load_error_message = "simulated page load failure";
3208 let fields = Fields::from(vec![ArrowField::new("vector", DataType::Float32, true)]);
3209 let root_decoder = SimpleStructDecoder::new(fields, total_rows);
3210 let (tx, rx) = unbounded_channel();
3211
3212 tx.send(Ok(DecoderMessage {
3213 scheduled_so_far: scheduled_rows,
3214 decoders: vec![MessageType::DecoderReady(DecoderReady {
3215 decoder: Box::new(FailingPageDecoder::new(
3216 DataType::Float32,
3217 page_rows,
3218 load_error_message,
3219 )),
3220 path: VecDeque::from([0]),
3221 })],
3222 }))
3223 .unwrap();
3224 drop(tx);
3225
3226 let stream =
3227 BatchDecodeStream::new(rx, rows_per_batch, total_rows, root_decoder).into_stream();
3228 let mut batches = stream.map(|task| task.task).buffered(batch_readahead);
3229
3230 let err = batches
3231 .next()
3232 .await
3233 .expect("stream should emit the array page-load error")
3234 .unwrap_err();
3235 assert!(
3236 err.to_string().contains(load_error_message),
3237 "unexpected error: {}",
3238 err
3239 );
3240 assert!(
3241 batches.next().await.is_none(),
3242 "stream should stop after the array page-load error"
3243 );
3244 }
3245
3246 #[tokio::test]
3247 async fn test_structural_stream_stops_on_load_error() {
3248 let rows_per_batch = 1;
3249 let total_rows = 2;
3250 let scheduled_rows = 1;
3251 let batch_readahead = 2;
3252 let load_error_message = "simulated page load failure";
3253 let fields = Fields::from(vec![ArrowField::new("vector", DataType::Float32, true)]);
3254 let root_decoder = StructuralStructDecoder::new(fields, false, true).unwrap();
3255 let (tx, rx) = unbounded_channel();
3256 let failed_page = async move { Err(Error::io(load_error_message)) }.boxed();
3257
3258 tx.send(Ok(DecoderMessage {
3259 scheduled_so_far: scheduled_rows,
3260 decoders: vec![MessageType::UnloadedPage(UnloadedPageShard(failed_page))],
3261 }))
3262 .unwrap();
3263 drop(tx);
3264
3265 let stream = StructuralBatchDecodeStream::new(
3266 rx,
3267 rows_per_batch,
3268 total_rows,
3269 root_decoder,
3270 true,
3271 None,
3272 )
3273 .into_stream();
3274 let mut batches = stream.map(|task| task.task).buffered(batch_readahead);
3275
3276 let err = batches
3277 .next()
3278 .await
3279 .expect("stream should emit the page-load error")
3280 .unwrap_err();
3281 assert!(
3282 err.to_string().contains(load_error_message),
3283 "unexpected error: {}",
3284 err
3285 );
3286 assert!(
3287 batches.next().await.is_none(),
3288 "stream should stop after the page-load error"
3289 );
3290 }
3291
3292 #[test]
3293 fn test_coalesce_indices_to_ranges_with_single_index() {
3294 let indices = vec![1];
3295 let ranges = DecodeBatchScheduler::indices_to_ranges(&indices);
3296 assert_eq!(ranges, vec![1..2]);
3297 }
3298
3299 #[test]
3300 fn test_coalesce_indices_to_ranges() {
3301 let indices = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
3302 let ranges = DecodeBatchScheduler::indices_to_ranges(&indices);
3303 assert_eq!(ranges, vec![1..10]);
3304 }
3305
3306 #[test]
3307 fn test_coalesce_indices_to_ranges_with_gaps() {
3308 let indices = vec![1, 2, 3, 5, 6, 7, 9];
3309 let ranges = DecodeBatchScheduler::indices_to_ranges(&indices);
3310 assert_eq!(ranges, vec![1..4, 5..8, 9..10]);
3311 }
3312
3313 #[test]
3314 fn test_estimate_bytes_per_row() {
3315 assert_eq!(estimate_bytes_per_row(&DataType::Int32), 4.0);
3316 assert_eq!(estimate_bytes_per_row(&DataType::Int64), 8.0);
3317 assert_eq!(estimate_bytes_per_row(&DataType::Float32), 4.0);
3318 assert_eq!(estimate_bytes_per_row(&DataType::Boolean), 1.0 / 8.0);
3319 assert_eq!(estimate_bytes_per_row(&DataType::Utf8), 64.0);
3320 assert_eq!(estimate_bytes_per_row(&DataType::Binary), 64.0);
3321 let struct_type = DataType::Struct(Fields::from(vec![
3323 ArrowField::new("a", DataType::Int32, false),
3324 ArrowField::new("b", DataType::Int32, false),
3325 ArrowField::new("c", DataType::Int32, false),
3326 ArrowField::new("d", DataType::Int32, false),
3327 ]));
3328 assert_eq!(estimate_bytes_per_row(&struct_type), 16.0);
3329 }
3330
3331 async fn decode_batches_with_byte_limit(
3334 batch: &RecordBatch,
3335 batch_size: u32,
3336 batch_size_bytes: Option<u64>,
3337 ) -> Vec<RecordBatch> {
3338 use crate::{
3339 encoder::{EncodingOptions, encode_batch},
3340 testing::{TestEncoding, test_encoding_strategy},
3341 };
3342
3343 let version = TestEncoding::StructuralU16;
3344 let options = EncodingOptions::default();
3345 let strategy = test_encoding_strategy(version);
3346 let schema = Schema::try_from(batch.schema().as_ref()).unwrap();
3347 let encoded = encode_batch(batch, Arc::new(schema.clone()), strategy.as_ref(), &options)
3348 .await
3349 .unwrap();
3350
3351 let io_scheduler =
3352 Arc::new(BufferScheduler::new(encoded.data.clone())) as Arc<dyn EncodingsIo>;
3353 let cache = Arc::new(lance_core::cache::LanceCache::with_capacity(
3354 128 * 1024 * 1024,
3355 ));
3356 let decoder_plugins = Arc::new(DecoderPlugins::default());
3357
3358 let mut decode_scheduler = DecodeBatchScheduler::try_new(
3359 encoded.schema.as_ref(),
3360 &encoded.top_level_columns,
3361 &encoded.page_table,
3362 &vec![],
3363 encoded.num_rows,
3364 decoder_plugins,
3365 io_scheduler.clone(),
3366 cache,
3367 &FilterExpression::no_filter(),
3368 &DecoderConfig::default(),
3369 )
3370 .await
3371 .unwrap();
3372
3373 let (tx, rx) = unbounded_channel();
3374 decode_scheduler.schedule_range(
3375 0..encoded.num_rows,
3376 &FilterExpression::no_filter(),
3377 tx,
3378 io_scheduler,
3379 );
3380
3381 let mut decode_stream = create_decode_stream(
3382 &encoded.schema,
3383 encoded.num_rows,
3384 batch_size,
3385 true,
3386 true,
3387 true,
3388 rx,
3389 batch_size_bytes,
3390 )
3391 .unwrap();
3392
3393 let mut batches = Vec::new();
3394 while let Some(task) = decode_stream.next().await {
3395 batches.push(task.task.await.unwrap());
3396 }
3397 batches
3398 }
3399
3400 #[tokio::test]
3401 async fn test_byte_sized_batches_fixed_width() {
3402 use arrow_array::Int32Array;
3403
3404 let num_rows: i32 = 1000;
3406 let arrays: Vec<Arc<dyn arrow_array::Array>> = (0..4)
3407 .map(|col| {
3408 Arc::new(Int32Array::from_iter_values(
3409 (0..num_rows).map(move |row| row * 10 + col),
3410 )) as _
3411 })
3412 .collect();
3413
3414 let schema = Arc::new(ArrowSchema::new(vec![
3415 ArrowField::new("a", DataType::Int32, false),
3416 ArrowField::new("b", DataType::Int32, false),
3417 ArrowField::new("c", DataType::Int32, false),
3418 ArrowField::new("d", DataType::Int32, false),
3419 ]));
3420 let input_batch = RecordBatch::try_new(schema, arrays).unwrap();
3421
3422 let batches =
3424 decode_batches_with_byte_limit(&input_batch, 1024, Some(1600)).await;
3425
3426 assert_eq!(batches.len(), 10);
3428 for (i, batch) in batches.iter().enumerate() {
3429 assert_eq!(
3430 batch.num_rows(),
3431 100,
3432 "batch {i} should have 100 rows, got {}",
3433 batch.num_rows()
3434 );
3435 }
3436
3437 let all_batches: Vec<&RecordBatch> = batches.iter().collect();
3439 let concatenated =
3440 arrow_select::concat::concat_batches(&batches[0].schema(), all_batches.iter().copied())
3441 .unwrap();
3442 assert_eq!(concatenated.num_rows(), num_rows as usize);
3443 for col in 0..4 {
3444 assert_eq!(
3445 concatenated.column(col).as_ref(),
3446 input_batch.column(col).as_ref(),
3447 "column {col} roundtrip mismatch"
3448 );
3449 }
3450 }
3451
3452 #[tokio::test]
3453 async fn test_byte_sized_batches_none_unchanged() {
3454 use arrow_array::Int32Array;
3455
3456 let num_rows: i32 = 1000;
3458 let arrays: Vec<Arc<dyn arrow_array::Array>> = (0..2)
3459 .map(|col| {
3460 Arc::new(Int32Array::from_iter_values(
3461 (0..num_rows).map(move |row| row * 10 + col),
3462 )) as _
3463 })
3464 .collect();
3465
3466 let schema = Arc::new(ArrowSchema::new(vec![
3467 ArrowField::new("x", DataType::Int32, false),
3468 ArrowField::new("y", DataType::Int32, false),
3469 ]));
3470 let input_batch = RecordBatch::try_new(schema, arrays).unwrap();
3471
3472 let batches = decode_batches_with_byte_limit(&input_batch, 250, None).await;
3474 assert_eq!(batches.len(), 4);
3475 for (i, batch) in batches.iter().enumerate() {
3476 assert_eq!(
3477 batch.num_rows(),
3478 250,
3479 "batch {i} should have 250 rows, got {}",
3480 batch.num_rows()
3481 );
3482 }
3483 }
3484
3485 #[tokio::test]
3486 async fn test_byte_sized_batches_respect_row_limit() {
3487 use arrow_array::Int32Array;
3488
3489 let num_rows: i32 = 1000;
3490 let schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
3491 "x",
3492 DataType::Int32,
3493 false,
3494 )]));
3495 let input_batch = RecordBatch::try_new(
3496 schema,
3497 vec![Arc::new(Int32Array::from_iter_values(0..num_rows))],
3498 )
3499 .unwrap();
3500
3501 let batches =
3503 decode_batches_with_byte_limit(&input_batch, 100, Some(10_000)).await;
3504 assert_eq!(batches.len(), 10);
3505 assert!(batches.iter().all(|batch| batch.num_rows() == 100));
3506 }
3507
3508 #[tokio::test]
3509 async fn test_byte_sized_batches_feedback_convergence() {
3510 use arrow_array::StringArray;
3511
3512 let num_rows = 500;
3516 let value: String = "x".repeat(100);
3517 let arrays: Vec<Arc<dyn arrow_array::Array>> = vec![Arc::new(StringArray::from(
3518 (0..num_rows).map(|_| value.as_str()).collect::<Vec<_>>(),
3519 ))];
3520 let schema = Arc::new(ArrowSchema::new(vec![ArrowField::new(
3521 "s",
3522 DataType::Utf8,
3523 false,
3524 )]));
3525 let input_batch = RecordBatch::try_new(schema, arrays).unwrap();
3526
3527 let target_bytes: u64 = 5000;
3531 let batches = decode_batches_with_byte_limit(
3532 &input_batch,
3533 1024,
3534 Some(target_bytes),
3535 )
3536 .await;
3537
3538 let all_batches: Vec<&RecordBatch> = batches.iter().collect();
3540 let concatenated =
3541 arrow_select::concat::concat_batches(&batches[0].schema(), all_batches.iter().copied())
3542 .unwrap();
3543 assert_eq!(concatenated.num_rows(), num_rows as usize);
3544 assert_eq!(
3545 concatenated.column(0).as_ref(),
3546 input_batch.column(0).as_ref()
3547 );
3548
3549 assert!(
3552 batches.len() >= 2,
3553 "need at least 2 batches to test convergence"
3554 );
3555 if batches.len() >= 3 {
3558 let second_batch_rows = batches[1].num_rows();
3559 let third_batch_rows = batches[2].num_rows();
3560 assert!(
3562 (40..=60).contains(&second_batch_rows),
3563 "second batch should be near 50 rows, got {second_batch_rows}"
3564 );
3565 assert!(
3566 (40..=60).contains(&third_batch_rows),
3567 "third batch should be near 50 rows, got {third_batch_rows}"
3568 );
3569 }
3570 }
3571}