blob: 078f94229781ee28cb67b8ea3f2cd18d1d761549 [file]
// 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(),
)
}
}