diff --git a/rust/lancedb/src/remote/db.rs b/rust/lancedb/src/remote/db.rs index da9a4b09b..c7ff15b01 100644 --- a/rust/lancedb/src/remote/db.rs +++ b/rust/lancedb/src/remote/db.rs @@ -91,6 +91,10 @@ impl ServerVersion { pub fn support_fts_document_granularity(&self) -> bool { self.0 >= semver::Version::new(0, 6, 0) } + + pub fn support_create_index_replace_false(&self) -> bool { + self.0 >= semver::Version::new(0, 5, 1) + } } pub const OPT_REMOTE_PREFIX: &str = "remote_database_"; diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index 0911f1c01..4c7c88ec1 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -508,6 +508,12 @@ impl RemoteTable { }); } }; + if !index.replace && !self.server_version.support_create_index_replace_false() { + return Err(Error::NotSupported { + message: "create-index replace=false requires remote server version 0.5.1 or later" + .into(), + }); + } if matches!( &index.index, Index::FTS(params) if params.get_document_granularity().is_list_element() @@ -6083,29 +6089,33 @@ mod tests { #[tokio::test] async fn test_create_index_forwards_replace_false() { - let table = Table::new_with_handler("my_table", move |request| { - assert_eq!(request.method(), "POST"); - match request.url().path() { - "/v1/table/my_table/describe/" => { - let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]); - http::Response::builder() - .status(200) - .body(describe_response(&schema)) - .unwrap() - } - "/v1/table/my_table/create_index/" => { - let body = request.body().unwrap().as_bytes().unwrap(); - let body: serde_json::Value = serde_json::from_slice(body).unwrap(); - assert_eq!(body["replace"], json!(false)); + let table = Table::new_with_handler_version( + "my_table", + semver::Version::new(0, 5, 1), + move |request| { + assert_eq!(request.method(), "POST"); + match request.url().path() { + "/v1/table/my_table/describe/" => { + let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]); + http::Response::builder() + .status(200) + .body(describe_response(&schema)) + .unwrap() + } + "/v1/table/my_table/create_index/" => { + let body = request.body().unwrap().as_bytes().unwrap(); + let body: serde_json::Value = serde_json::from_slice(body).unwrap(); + assert_eq!(body["replace"], json!(false)); - http::Response::builder() - .status(200) - .body("{}".to_string()) - .unwrap() + http::Response::builder() + .status(200) + .body("{}".to_string()) + .unwrap() + } + path => panic!("Unexpected path: {}", path), } - path => panic!("Unexpected path: {}", path), - } - }); + }, + ); table .create_index(&["a"], Index::BTree(Default::default())) @@ -6115,6 +6125,26 @@ mod tests { .unwrap(); } + #[tokio::test] + async fn test_create_index_replace_false_rejects_unsupported_server() { + let table = Table::new_with_handler("my_table", |_| -> http::Response { + panic!("unsupported replace=false should be rejected before issuing remote requests") + }); + + let err = table + .create_index(&["a"], Index::BTree(Default::default())) + .replace(false) + .execute() + .await + .unwrap_err(); + + assert!( + matches!(&err, Error::NotSupported { message } + if message.contains("replace=false requires remote server version 0.5.1")), + "got {err:?}" + ); + } + #[tokio::test] async fn test_create_index_returns_job() { let describe_calls = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));