From dbe93edb12e5dc53777e1e4d55e086f806d4e3b7 Mon Sep 17 00:00:00 2001 From: Wyatt Alt Date: Sun, 16 Aug 2026 13:04:46 -0700 Subject: [PATCH] fix: refuse a remote table as a materialized view before any request The connection-level entry points hold a local-only boundary, but from_table does not, and that is the entry both bindings use to refresh. A remote table therefore reached a describe request and came back as a plain table rather than as unsupported, so the caller paid a round trip for a misleading error. Check the boundary before the schema read. Co-Authored-By: Claude Fable 5 --- rust/lancedb/src/materialized_view.rs | 7 +++++++ rust/lancedb/src/remote/table.rs | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/rust/lancedb/src/materialized_view.rs b/rust/lancedb/src/materialized_view.rs index 7231edf80..bf228f0f0 100644 --- a/rust/lancedb/src/materialized_view.rs +++ b/rust/lancedb/src/materialized_view.rs @@ -656,6 +656,13 @@ impl MaterializedView { /// [`Error::NotSupported`] for a view whose kind this version cannot /// refresh. pub async fn from_table(table: Table) -> Result { + // Same local-only boundary the connection-level entry points hold, + // applied before the schema read so a remote table costs no request. + if table.as_native().is_none() { + return Err(Error::NotSupported { + message: "materialized views are supported only on local databases".into(), + }); + } let schema = table.schema().await?; match materialized_view_kind(schema.metadata())? { Some(MaterializedViewKind::Select(definition)) => Ok(Self { table, definition }), diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index a0a4cebc2..b46ea4fbb 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -10156,6 +10156,20 @@ mod tests { ); } + #[tokio::test] + async fn test_materialized_view_refused_without_a_request() { + // Materialized views are local-only. The table-level entry the + // bindings use must refuse a remote table before reading its schema, + // so the panicking handler is the assertion. + let table = Table::new_with_handler("my_table", |request| -> http::Response { + panic!("unexpected request: {}", request.url().path()) + }); + let err = crate::MaterializedView::from_table(table) + .await + .unwrap_err(); + assert!(matches!(err, Error::NotSupported { .. }), "got {err:?}"); + } + #[tokio::test] async fn test_create_branch_empty_name_rejected_client_side() { use lance::dataset::refs::Ref;