Lists files with cache support, using prefix-aware lookups. # Arguments `ctx` - The session context `store` - The object store to list from `table_base_path` - The table's base path (the stable cache key) `prefix` - Optional prefix relative to table base for filtering results # Cache Behavior: The cache key is always `table_base_path`. When a prefix-filtered listing is requested via `prefix`, th
(
ctx: &'b dyn Session,
store: &'b dyn ObjectStore,
table_ref: Option<&TableReference>,
table_base_path: &Path,
prefix: Option<&Path>,
)
| 377 | /// On cache miss, the full table is always listed and cached, ensuring |
| 378 | /// subsequent prefix queries can be served from cache. |
| 379 | async fn list_with_cache<'b>( |
| 380 | ctx: &'b dyn Session, |
| 381 | store: &'b dyn ObjectStore, |
| 382 | table_ref: Option<&TableReference>, |
| 383 | table_base_path: &Path, |
| 384 | prefix: Option<&Path>, |
| 385 | ) -> Result<BoxStream<'b, Result<ObjectMeta>>> { |
| 386 | // Build the full listing path (table_base + prefix) |
| 387 | let full_prefix = match prefix { |
| 388 | Some(p) => { |
| 389 | let mut parts: Vec<_> = table_base_path.parts().collect(); |
| 390 | parts.extend(p.parts()); |
| 391 | Path::from_iter(parts) |
| 392 | } |
| 393 | None => table_base_path.clone(), |
| 394 | }; |
| 395 | |
| 396 | match ctx.runtime_env().cache_manager.get_list_files_cache() { |
| 397 | None => Ok(store |
| 398 | .list(Some(&full_prefix)) |
| 399 | .map(|res| res.map_err(|e| DataFusionError::ObjectStore(Box::new(e)))) |
| 400 | .boxed()), |
| 401 | Some(cache) => { |
| 402 | // Build the filter prefix (only Some if prefix was requested) |
| 403 | let filter_prefix = prefix.is_some().then(|| full_prefix.clone()); |
| 404 | |
| 405 | let table_scoped_base_path = TableScopedPath { |
| 406 | table: table_ref.cloned(), |
| 407 | path: table_base_path.clone(), |
| 408 | }; |
| 409 | |
| 410 | // Try cache lookup - get returns CachedFileList |
| 411 | let vec = if let Some(cached) = cache.get(&table_scoped_base_path) { |
| 412 | debug!("Hit list files cache"); |
| 413 | cached.files_matching_prefix(&filter_prefix) |
| 414 | } else { |
| 415 | // Cache miss - always list and cache the full table |
| 416 | // This ensures we have complete data for future prefix queries |
| 417 | let mut vec = store |
| 418 | .list(Some(table_base_path)) |
| 419 | .try_collect::<Vec<ObjectMeta>>() |
| 420 | .await?; |
| 421 | vec.shrink_to_fit(); // Right-size before caching |
| 422 | let cached: CachedFileList = vec.into(); |
| 423 | let result = cached.files_matching_prefix(&filter_prefix); |
| 424 | cache.put(&table_scoped_base_path, cached); |
| 425 | result |
| 426 | }; |
| 427 | Ok( |
| 428 | futures::stream::iter(Arc::unwrap_or_clone(vec).into_iter().map(Ok)) |
| 429 | .boxed(), |
| 430 | ) |
| 431 | } |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | /// Creates a file URL from a potentially relative filesystem path |
| 436 | #[cfg(not(target_arch = "wasm32"))] |
no test coverage detected
searching dependent graphs…