Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4fad44b
feat(datafusion): support the paimon_incremental_query table function
jackylee-ch Oct 5, 2026
692d8fc
Merge branch 'main' into feat/datafusion-incremental-query
jackylee-ch Oct 15, 2026
9568181
fix: stream incremental reads and decode them as ordinary reads
jackylee-ch Oct 15, 2026
8f6a34b
Merge branch 'main' into feat/datafusion-incremental-query
jackylee-ch Oct 21, 2026
f8016e1
fix(datafusion): read incremental queries in the calling session
jackylee-ch Oct 21, 2026
9235fe8
Merge remote-tracking branch 'upstream/main' into feat/datafusion-inc…
jackylee-ch Oct 23, 2026
8f26c02
fix(core): decode change events of BLOB/VECTOR rewrites file by file
jackylee-ch Oct 23, 2026
c9558be
Merge remote-tracking branch 'upstream/main' into feat/datafusion-inc…
jackylee-ch Oct 29, 2026
60fcd30
fix(core): decode change events of a field stored two ways file by file
jackylee-ch Oct 29, 2026
792dc57
fix(core): keep change events a filter matches in one of their files
jackylee-ch Oct 30, 2026
597f7a5
fix(datafusion): return _ROW_ID from paimon_incremental_query
jackylee-ch Oct 30, 2026
7d4701d
fix: count the rows change events return
jackylee-ch Oct 30, 2026
5b93dde
fix(core): stop a limit at the rows change events return at least
jackylee-ch Oct 30, 2026
d6f8693
fix(core): chunk change events of BLOB or VECTOR files alone
jackylee-ch Oct 30, 2026
ff88c78
docs: match change events read file by file back to their rows
jackylee-ch Oct 31, 2026
c141ccb
fix(datafusion): refuse writes and vector_search under a session window
jackylee-ch Oct 31, 2026
c0d116b
docs: say what a delta range returns of deleted rows and partitions
jackylee-ch Oct 31, 2026
87ebeaa
fix(datafusion): refuse a window the table stores, where rows are loo…
jackylee-ch Oct 31, 2026
a821eb8
docs: join Python rows of a range by distinct `_ROW_ID`s
jackylee-ch Oct 31, 2026
d12758f
Merge remote-tracking branch 'upstream/main' into feat/datafusion-inc…
jackylee-ch Oct 31, 2026
510cff4
fix(core): refuse blob views of an upstream that stores a window
jackylee-ch Oct 31, 2026
f847ccb
fix(datafusion): name a `$branch_` table once in window refusals
jackylee-ch Oct 31, 2026
c8f8b02
docs: say blob views of an upstream that stores a window fail to resolve
jackylee-ch Oct 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bindings/python/python/pypaimon_rust/datafusion.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ class TableScan:
def with_row_position_slice(self, start: int, end: int) -> "TableScan": ...
def with_row_position_shard(self, index: int, count: int) -> "TableScan": ...
def with_chunk_shuffle(self, seed: str, chunk_size: int) -> "TableScan":
"""Deterministically shuffle fixed-live-row chunks. ``seed`` is a decimal
"""Deterministically shuffle fixed-live-row chunks; on Data Evolution
tables chunks count row ids. ``seed`` is a decimal
integer string so arbitrarily large seeds keep Python ``random.Random``
semantics."""
...
Expand Down
3 changes: 2 additions & 1 deletion bindings/python/src/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,8 @@ impl PyTableScan {
Ok(slf)
}

/// Deterministically shuffle fixed-live-row chunks. `seed` is a decimal
/// Deterministically shuffle fixed-live-row chunks; on Data Evolution
/// tables chunks count row ids. `seed` is a decimal
/// Python integer string so arbitrarily large seeds retain Python's
/// `random.Random` semantics.
fn with_chunk_shuffle(
Expand Down
16 changes: 16 additions & 0 deletions crates/integrations/datafusion/src/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,22 @@ impl PaimonCatalogProvider {
self
}

pub(crate) fn catalog(&self) -> &Arc<dyn Catalog> {
&self.catalog
}

/// The session's dynamic options as they stand now.
pub(crate) fn session_options(&self) -> HashMap<String, String> {
self.dynamic_options
.read()
.unwrap_or_else(|e| e.into_inner())
.clone()
}

pub(crate) fn blob_reader_registry(&self) -> &BlobReaderRegistry {
&self.blob_reader_registry
}

/// Register an engine for a table type the Paimon reader cannot serve
/// (e.g. [`PaimonTableType::IcebergTable`]); everything else takes the
/// Paimon path unchanged. Kept inside the provider so the registered
Expand Down
19 changes: 16 additions & 3 deletions crates/integrations/datafusion/src/full_text_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
//!
//! Reference: [PaimonTableValuedFunctions.scala](https://github.com/apache/paimon/blob/master/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/PaimonTableValuedFunctions.scala)

use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;

Expand All @@ -33,7 +34,7 @@ use datafusion::catalog::Session;
use datafusion::catalog::TableFunctionImpl;
use datafusion::common::project_schema;
use datafusion::datasource::{TableProvider, TableType};
use datafusion::error::Result as DFResult;
use datafusion::error::{DataFusionError, Result as DFResult};
use datafusion::logical_expr::{Expr, TableProviderFilterPushDown};
use datafusion::physical_plan::empty::EmptyExec;
use datafusion::physical_plan::ExecutionPlan;
Expand All @@ -46,7 +47,7 @@ use crate::table::{PaimonScanBuilder, PaimonTableProvider};
use crate::table_function_args::{
extract_int_literal, extract_string_literal, parse_table_identifier,
};
use crate::table_loader::load_data_table_for_read;
use crate::table_loader::{ensure_no_incremental_window, load_data_table_for_read};

const FUNCTION_NAME: &str = "full_text_search";

Expand Down Expand Up @@ -111,7 +112,19 @@ impl TableFunctionImpl for FullTextSearchFunction {

let catalog = Arc::clone(&self.catalog);
let table = block_on_with_runtime(
async move { load_data_table_for_read(&catalog, &identifier, FUNCTION_NAME).await },
async move {
let table = load_data_table_for_read(&catalog, &identifier, FUNCTION_NAME).await?;
// The index is searched at one snapshot, while a window stored
// on the table would make the read of its hits return that
// window's changes. Session options do not apply here.
ensure_no_incremental_window(
&format!("{FUNCTION_NAME} searches one snapshot and cannot run"),
&HashMap::new(),
&identifier,
&table,
)?;
Ok::<_, DataFusionError>(table)
},
"full_text_search: catalog access thread panicked",
)?;

Expand Down
16 changes: 14 additions & 2 deletions crates/integrations/datafusion/src/hybrid_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ use crate::table::{
use crate::table_function_args::{
extract_int_literal, extract_string_literal, parse_table_identifier,
};
use crate::table_loader::load_data_table_for_read;
use crate::table_loader::{ensure_no_incremental_window, load_data_table_for_read};

const FUNCTION_NAME: &str = "hybrid_search";
const SEARCH_SCORE_COLUMN: &str = "__paimon_search_score";
Expand Down Expand Up @@ -133,7 +133,19 @@ impl TableFunctionImpl for HybridSearchFunction {
parse_table_identifier(FUNCTION_NAME, &table_name, &self.default_database)?;
let catalog = Arc::clone(&self.catalog);
let table = block_on_with_runtime(
async move { load_data_table_for_read(&catalog, &identifier, FUNCTION_NAME).await },
async move {
let table = load_data_table_for_read(&catalog, &identifier, FUNCTION_NAME).await?;
// The indexes are searched at one snapshot, while a window
// stored on the table would make the read of their hits return
// that window's changes. Session options do not apply here.
ensure_no_incremental_window(
&format!("{FUNCTION_NAME} searches one snapshot and cannot run"),
&HashMap::new(),
&identifier,
&table,
)?;
Ok::<_, DataFusionError>(table)
},
"hybrid_search: catalog access thread panicked",
)?;

Expand Down
Loading
Loading