Skip to content

Commit 2e8992e

Browse files
authored
Merge pull request #13 from datafusion-contrib/12-bump-datafusion-v54
Bump DataFusion to 54.0.0 (#12)
2 parents a1702c4 + 7cffd91 commit 2e8992e

27 files changed

Lines changed: 650 additions & 1350 deletions

Cargo.toml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,25 @@ documentation = "https://docs.rs/datafusion-index-provider"
1010
keywords = ["datafusion", "index", "database", "query", "analytics"]
1111
categories = ["database", "data-structures"]
1212
readme = "README.md"
13-
rust-version = "1.75"
13+
rust-version = "1.88"
1414

1515
[dependencies]
1616
async-trait = "0.1.89"
17-
datafusion = { version = "52.1.0", default-features = false }
18-
datafusion-common = { version = "52.1.0", default-features = false }
17+
datafusion = { version = "54.0.0", default-features = false }
18+
datafusion-common = { version = "54.0.0", default-features = false }
1919
futures = "0.3.32"
2020
futures-util = "0.3.32"
2121
futures-core = "0.3.32"
22-
log = "0.4.29"
22+
log = "0.4.33"
2323

2424
[dev-dependencies]
25-
tokio = { version = "1.49.0", features = ["macros", "rt-multi-thread"] }
26-
env_logger = "0.11.9"
25+
tokio = { version = "1.52.3", features = ["macros", "rt-multi-thread"] }
26+
env_logger = "0.11.11"
2727
parking_lot = "0.12.5"
28-
datafusion = { version = "52.1.0", default-features = false, features = ["sql"] }
28+
datafusion = { version = "54.0.0", default-features = false, features = ["sql"] }
29+
datafusion-sqllogictest = { version = "54.0.0", default-features = false }
30+
sqllogictest = "0.29"
31+
indicatif = "0.18"
32+
33+
[lints.clippy]
34+
pedantic = { level = "warn", priority = -1 }

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,21 @@ cargo test
222222
# Run with logging
223223
RUST_LOG=debug cargo test
224224

225-
# Run integration tests only
226-
cargo test --test integration_tests
225+
# Run the SQL logic tests only
226+
cargo test --test sqllogictests
227+
228+
# Regenerate the expected .slt results after an intended change
229+
SLT_COMPLETE=1 cargo test --test sqllogictests
227230
```
228231

229232
The test suite includes:
230233
- 27 unit tests covering execution plan generation and streaming
231-
- 36 integration tests covering query scenarios from simple to deeply nested, with both single and composite primary keys
234+
- SQL logic tests (`tests/slt/*.slt`, run via [sqllogictest](https://github.com/risinglightdb/sqllogictest-rs)) covering query scenarios from simple to deeply nested, with both single and composite primary keys
232235

233236
## Compatibility
234237

235-
- **DataFusion**: 52.x
236-
- **Rust**: 1.75+ (MSRV)
238+
- **DataFusion**: 54.x
239+
- **Rust**: 1.88+ (MSRV)
237240
- **Arrow**: Compatible with DataFusion's Arrow version
238241

239242
## Architecture Details

examples/basic.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
//! Basic example demonstrating how to use `datafusion-index-provider`.
1919
//!
2020
//! This example creates an in-memory table with an age index and a department index,
21-
//! registers it as a DataFusion table, and runs queries that leverage index-based scans.
21+
//! registers it as a `DataFusion` table, and runs queries that leverage index-based scans.
2222
2323
use std::any::Any;
2424
use std::collections::{BTreeMap, BTreeSet};
@@ -53,20 +53,20 @@ use datafusion_index_provider::{IndexedTableProvider, RecordFetcher, UnionMode};
5353
#[derive(Debug)]
5454
struct AgeIndex {
5555
/// age -> list of row ids
56-
index: BTreeMap<i32, Vec<i32>>,
56+
index: BTreeMap<i32, Vec<u64>>,
5757
}
5858

5959
impl AgeIndex {
60-
fn new(ages: &Int32Array, ids: &Int32Array) -> Self {
61-
let mut index: BTreeMap<i32, Vec<i32>> = BTreeMap::new();
60+
fn new(ages: &Int32Array, ids: &UInt64Array) -> Self {
61+
let mut index: BTreeMap<i32, Vec<u64>> = BTreeMap::new();
6262
for i in 0..ages.len() {
6363
index.entry(ages.value(i)).or_default().push(ids.value(i));
6464
}
6565
Self { index }
6666
}
6767

6868
fn matching_ids(&self, filters: &[Expr], limit: Option<usize>) -> Vec<u64> {
69-
let mut ids: BTreeSet<i32> = BTreeSet::new();
69+
let mut ids: BTreeSet<u64> = BTreeSet::new();
7070
for filter in filters {
7171
if let Expr::BinaryExpr(be) = filter {
7272
if let (Expr::Column(c), Expr::Literal(ScalarValue::Int32(Some(v)), _)) =
@@ -82,7 +82,7 @@ impl AgeIndex {
8282
}
8383
}
8484
Operator::Gt => {
85-
ids.extend(self.index.range((v + 1)..).flat_map(|(_, l)| l))
85+
ids.extend(self.index.range((v + 1)..).flat_map(|(_, l)| l));
8686
}
8787
Operator::GtEq => ids.extend(self.index.range(v..).flat_map(|(_, l)| l)),
8888
Operator::Lt => ids.extend(self.index.range(..v).flat_map(|(_, l)| l)),
@@ -92,7 +92,7 @@ impl AgeIndex {
9292
}
9393
}
9494
}
95-
let mut result: Vec<u64> = ids.into_iter().map(|id| id as u64).collect();
95+
let mut result: Vec<u64> = ids.into_iter().collect();
9696
if let Some(l) = limit {
9797
result.truncate(l);
9898
}
@@ -104,16 +104,16 @@ impl Index for AgeIndex {
104104
fn as_any(&self) -> &dyn Any {
105105
self
106106
}
107-
fn name(&self) -> &str {
107+
fn name(&self) -> &'static str {
108108
"age_index"
109109
}
110110
fn index_schema(&self) -> SchemaRef {
111111
create_index_schema([Field::new("id", DataType::UInt64, false)])
112112
}
113-
fn table_name(&self) -> &str {
113+
fn table_name(&self) -> &'static str {
114114
"employees"
115115
}
116-
fn column_name(&self) -> &str {
116+
fn column_name(&self) -> &'static str {
117117
"age"
118118
}
119119
fn scan(
@@ -168,7 +168,7 @@ impl RecordFetcher for InMemoryFetcher {
168168
.expect("expected UInt64Array for primary key column");
169169

170170
// Convert 1-based ids to 0-based indices for arrow take
171-
let indices = Int32Array::from_iter_values(ids.iter().flatten().map(|id| (id - 1) as i32));
171+
let indices = UInt64Array::from_iter_values(ids.iter().flatten().map(|id| id - 1));
172172

173173
let columns: Result<Vec<ArrayRef>> = self
174174
.batch
@@ -200,9 +200,6 @@ struct EmployeeTable {
200200

201201
#[async_trait]
202202
impl TableProvider for EmployeeTable {
203-
fn as_any(&self) -> &dyn Any {
204-
self
205-
}
206203
fn schema(&self) -> SchemaRef {
207204
self.schema.clone()
208205
}
@@ -269,20 +266,17 @@ async fn main() -> Result<()> {
269266
],
270267
)?;
271268

272-
let ids = batch
273-
.column(0)
274-
.as_any()
275-
.downcast_ref::<Int32Array>()
276-
.unwrap();
277269
let ages = batch
278270
.column(2)
279271
.as_any()
280272
.downcast_ref::<Int32Array>()
281-
.unwrap();
273+
.expect("age column is an Int32Array");
274+
// Row identifiers matching the index schema's UInt64 primary key column.
275+
let pk_ids = UInt64Array::from(vec![1u64, 2, 3, 4, 5]);
282276

283277
let provider = EmployeeTable {
284278
schema: schema.clone(),
285-
age_index: Arc::new(AgeIndex::new(ages, ids)),
279+
age_index: Arc::new(AgeIndex::new(ages, &pk_ids)),
286280
fetcher: Arc::new(InMemoryFetcher {
287281
batch: batch.clone(),
288282
}),

flake.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/lib.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717

1818
#![warn(missing_docs)]
1919

20-
//! # DataFusion Index Provider
20+
//! # `DataFusion` Index Provider
2121
//!
2222
//! This crate provides a comprehensive framework for adding index-based scanning capabilities
23-
//! to DataFusion [`datafusion::datasource::TableProvider`]s. It enables efficient query execution
23+
//! to `DataFusion` [`datafusion::datasource::TableProvider`]s. It enables efficient query execution
2424
//! by leveraging secondary indexes to reduce I/O and improve query performance through a
2525
//! sophisticated two-phase execution model.
2626
//!
@@ -38,7 +38,7 @@
3838
//!
3939
//! ### Index Management
4040
//! - [`physical_plan::Index`]: Trait representing a physical index that can be scanned to retrieve primary key values
41-
//! - [`provider::IndexedTableProvider`]: Extension of DataFusion's `TableProvider` with index discovery
41+
//! - [`provider::IndexedTableProvider`]: Extension of `DataFusion`'s `TableProvider` with index discovery
4242
//! - [`types::IndexFilter`]: Enum representing filter operations that can be pushed down to indexes
4343
//!
4444
//! ### Execution Engine
@@ -57,7 +57,7 @@
5757
//! - **Default implementation**: Supports any predicate that references the index's column name
5858
//! - **Custom implementations**: Can implement sophisticated predicate analysis for complex index types
5959
//!
60-
//! ### IndexedTableProvider Filter Analysis
60+
//! ### `IndexedTableProvider` Filter Analysis
6161
//! The [`provider::IndexedTableProvider`] trait provides comprehensive filter analysis through
6262
//! `build_index_filter()`:
6363
//!
@@ -96,15 +96,15 @@
9696
//! The [`physical_plan::exec::fetch::RecordFetchExec`] generates
9797
//! optimized execution plans based on the [`types::IndexFilter`] structure:
9898
//!
99-
//! ### IndexFilter::Single - Direct Index Scan
99+
//! ### `IndexFilter::Single` - Direct Index Scan
100100
//!
101101
//! For simple conditions on a single indexed column:
102102
//! ```text
103103
//! RecordFetchExec
104104
//! └── IndexScanExec (target_index)
105105
//! ```
106106
//!
107-
//! ### IndexFilter::And - Index Intersection
107+
//! ### `IndexFilter::And` - Index Intersection
108108
//!
109109
//! For conjunctive conditions across multiple indexes, the system builds a left-deep tree
110110
//! of joins to intersect primary key values:
@@ -119,7 +119,7 @@
119119
//! └── IndexScanExec (col_c_index)
120120
//! ```
121121
//!
122-
//! ### IndexFilter::Or - Union with Deduplication
122+
//! ### `IndexFilter::Or` - Union with Deduplication
123123
//!
124124
//! For disjunctive conditions, the system uses `UnionExec` followed by `AggregateExec`
125125
//! for automatic primary key deduplication:
@@ -138,9 +138,9 @@
138138
//! ## Implementation Guide
139139
//!
140140
//! - **Implement the Index Trait**: Create indexes that can scan and return primary key values
141-
//! - **Implement the RecordFetcher Trait**: Define how to fetch complete records using primary key values
142-
//! - **Implement IndexedTableProvider**: Expose available indexes and filter analysis capabilities
143-
//! - **Update TableProvider Implementation**: Integrate index-based execution into your scan method
141+
//! - **Implement the `RecordFetcher` Trait**: Define how to fetch complete records using primary key values
142+
//! - **Implement `IndexedTableProvider`**: Expose available indexes and filter analysis capabilities
143+
//! - **Update `TableProvider` Implementation**: Integrate index-based execution into your scan method
144144
//!
145145
//! ## Performance Characteristics
146146
//!

0 commit comments

Comments
 (0)