| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| use super::data_evolution_reader::DataEvolutionReader; |
| use super::data_file_reader::DataFileReader; |
| use super::kv_file_reader::{KeyValueFileReader, KeyValueReadConfig}; |
| use super::read_builder::split_scan_predicates; |
| use super::{ArrowRecordBatchStream, Table}; |
| use crate::arrow::filtering::reader_pruning_predicates; |
| use crate::spec::{CoreOptions, DataField, Predicate}; |
| use crate::DataSplit; |
| |
| /// Table read: reads data from splits (e.g. produced by [TableScan::plan]). |
| /// |
| /// Reference: [pypaimon.read.table_read.TableRead](https://github.com/apache/paimon/blob/master/paimon-python/pypaimon/read/table_read.py) |
| #[derive(Debug, Clone)] |
| pub struct TableRead<'a> { |
| table: &'a Table, |
| read_type: Vec<DataField>, |
| data_predicates: Vec<Predicate>, |
| } |
| |
| impl<'a> TableRead<'a> { |
| /// Create a new TableRead with a specific read type (projected fields). |
| pub fn new( |
| table: &'a Table, |
| read_type: Vec<DataField>, |
| data_predicates: Vec<Predicate>, |
| ) -> Self { |
| Self { |
| table, |
| read_type, |
| data_predicates, |
| } |
| } |
| |
| /// Schema (fields) that this read will produce. |
| pub fn read_type(&self) -> &[DataField] { |
| &self.read_type |
| } |
| |
| /// Data predicates for read-side pruning. |
| pub fn data_predicates(&self) -> &[Predicate] { |
| &self.data_predicates |
| } |
| |
| /// Table for this read. |
| pub fn table(&self) -> &Table { |
| self.table |
| } |
| |
| /// Set a filter predicate for conservative read-side pruning. |
| pub fn with_filter(mut self, filter: Predicate) -> Self { |
| let (_, data_predicates) = split_scan_predicates(self.table, filter); |
| self.data_predicates = reader_pruning_predicates(data_predicates); |
| self |
| } |
| |
| /// Returns an [`ArrowRecordBatchStream`]. |
| pub fn to_arrow(&self, data_splits: &[DataSplit]) -> crate::Result<ArrowRecordBatchStream> { |
| let has_primary_keys = !self.table.schema.primary_keys().is_empty(); |
| let core_options = CoreOptions::new(self.table.schema.options()); |
| |
| // PK table with Deduplicate engine: splits containing level-0 files |
| // need KeyValueFileReader for sort-merge dedup; splits with only |
| // compacted files (level > 0) can use the faster DataFileReader. |
| // FirstRow engine falls through — scan already skips level-0. |
| if has_primary_keys |
| && core_options |
| .merge_engine() |
| .is_ok_and(|e| e == crate::spec::MergeEngine::Deduplicate) |
| { |
| return self.read_pk_deduplicate(data_splits, &core_options); |
| } |
| |
| if core_options.data_evolution_enabled() { |
| self.read_with_evolution(data_splits, &core_options) |
| } else { |
| self.read_raw(data_splits) |
| } |
| } |
| |
| /// Read PK table with Deduplicate engine: level-0 splits go through |
| /// KeyValueFileReader for sort-merge dedup, compacted splits use DataFileReader. |
| fn read_pk_deduplicate( |
| &self, |
| data_splits: &[DataSplit], |
| core_options: &CoreOptions, |
| ) -> crate::Result<ArrowRecordBatchStream> { |
| let mut kv_splits = Vec::new(); |
| let mut raw_splits = Vec::new(); |
| for split in data_splits { |
| if split.data_files().iter().any(|f| f.level == 0) { |
| kv_splits.push(split.clone()); |
| } else { |
| raw_splits.push(split.clone()); |
| } |
| } |
| |
| if raw_splits.is_empty() { |
| return self.read_kv(&kv_splits, core_options); |
| } |
| if kv_splits.is_empty() { |
| return self.read_raw(&raw_splits); |
| } |
| |
| let kv_stream = self.read_kv(&kv_splits, core_options)?; |
| let raw_stream = self.read_raw(&raw_splits)?; |
| Ok(Box::pin(futures::stream::select_all([ |
| kv_stream, raw_stream, |
| ]))) |
| } |
| |
| /// Read splits via KeyValueFileReader (sort-merge dedup). |
| fn read_kv( |
| &self, |
| splits: &[DataSplit], |
| core_options: &CoreOptions, |
| ) -> crate::Result<ArrowRecordBatchStream> { |
| let reader = KeyValueFileReader::new( |
| self.table.file_io.clone(), |
| KeyValueReadConfig { |
| schema_manager: self.table.schema_manager().clone(), |
| table_schema_id: self.table.schema().id(), |
| table_fields: self.table.schema.fields().to_vec(), |
| read_type: self.read_type().to_vec(), |
| predicates: self.data_predicates.clone(), |
| primary_keys: self.table.schema.trimmed_primary_keys(), |
| sequence_fields: core_options |
| .sequence_fields() |
| .iter() |
| .map(|s| s.to_string()) |
| .collect(), |
| }, |
| ); |
| reader.read(splits) |
| } |
| |
| /// Read with data-evolution support. |
| fn read_with_evolution( |
| &self, |
| data_splits: &[DataSplit], |
| core_options: &CoreOptions, |
| ) -> crate::Result<ArrowRecordBatchStream> { |
| let reader = DataEvolutionReader::new( |
| self.table.file_io.clone(), |
| self.table.schema_manager().clone(), |
| self.table.schema().id(), |
| self.table.schema.fields().to_vec(), |
| self.read_type().to_vec(), |
| core_options.blob_as_descriptor(), |
| core_options.blob_descriptor_fields(), |
| )?; |
| reader.read(data_splits) |
| } |
| |
| /// Read raw data files without dedup or evolution. |
| fn read_raw(&self, data_splits: &[DataSplit]) -> crate::Result<ArrowRecordBatchStream> { |
| self.new_data_file_reader().read(data_splits) |
| } |
| |
| fn new_data_file_reader(&self) -> DataFileReader { |
| DataFileReader::new( |
| self.table.file_io.clone(), |
| self.table.schema_manager().clone(), |
| self.table.schema().id(), |
| self.table.schema.fields().to_vec(), |
| self.read_type().to_vec(), |
| self.data_predicates.clone(), |
| ) |
| } |
| } |