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