From d07064ddbef57eab42e61ee3de87ef653f031ee7 Mon Sep 17 00:00:00 2001 From: Yang Cen Date: Wed, 12 Aug 2026 13:50:24 +0800 Subject: [PATCH] fix(storage): probe file object stores through providers --- rust/lancedb/src/table.rs | 64 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/rust/lancedb/src/table.rs b/rust/lancedb/src/table.rs index 878c3db68..24ca9972b 100644 --- a/rust/lancedb/src/table.rs +++ b/rust/lancedb/src/table.rs @@ -200,11 +200,8 @@ async fn table_storage_exists(uri: &str, params: ReadParams) -> Result { return Ok(false); } - let is_native_local = !requires_store_probe - && matches!( - object_store.scheme(), - "file" | "file+uring" | "file-object-store" - ); + let is_native_local = + !requires_store_probe && matches!(object_store.scheme(), "file" | "file+uring"); if is_native_local { let local_path = std::path::PathBuf::from(lance_io::local::to_local_path(&path)); // A local table URI intentionally grants access to this exact path; LanceDB does @@ -3984,6 +3981,63 @@ mod tests { ); } + /// `file-object-store` deliberately routes through the ObjectStore API, so its + /// provider remains authoritative even though its URI names a local path. + #[tokio::test] + async fn test_open_recovery_uses_custom_file_object_store_provider() { + use object_store::ObjectStoreExt as _; + + let tmp_dir = tempdir().unwrap(); + let dataset_path = tmp_dir.path().join("custom-object-store.lance"); + let path = dataset_path.to_str().unwrap().replace('\\', "/"); + let path_prefix = if path.starts_with('/') { "" } else { "/" }; + let url = url::Url::parse(&format!("file-object-store://{path_prefix}{path}")).unwrap(); + let uri = url.to_string(); + assert!(!dataset_path.exists()); + + let inner = Arc::new(object_store::memory::InMemory::new()); + let table_path = object_store::path::Path::from_url_path(url.path()).unwrap(); + inner + .put( + &table_path.clone().join("_marker"), + bytes::Bytes::new().into(), + ) + .await + .unwrap(); + let store = lance_io::object_store::ObjectStore::new( + inner, url, None, None, false, false, 1, 0, None, + ); + + let registry = Arc::new(lance_io::object_store::ObjectStoreRegistry::default()); + registry.insert( + "file-object-store", + Arc::new(FileSchemeMemoryProvider { store }), + ); + let read_params = ReadParams { + session: Some(Arc::new(lance::session::Session::new(0, 0, registry))), + ..Default::default() + }; + + let err = NativeTable::open_with_params( + &uri, + "custom-object-store", + Vec::new(), + None, + Some(read_params), + None, + None, + HashSet::new(), + None, + ) + .await + .unwrap_err(); + + assert!( + matches!(&err, Error::TableCorrupted { name, .. } if name == "custom-object-store"), + "got {err:?}" + ); + } + #[derive(Debug)] struct OpenRecoveryTestStore { inner: Arc,