From e489d0cd7b272bab25066c25a951fd2bac8f8c15 Mon Sep 17 00:00:00 2001 From: adenzhou1350 Date: Fri, 23 Oct 2026 02:43:07 +0800 Subject: [PATCH] fix: return errors for unsupported remote alteration types (#4430) RemoteTable::alter_columns currently unwraps the JsonDataType conversion. A valid Arrow input that the remote wire format cannot represent (for example Utf8View, BinaryView, or a struct containing Utf8View) therefore panics instead of returning an error. Propagate conversion/serialization failures as Error::InvalidInput, including the column path. Unsupported inputs are rejected before any HTTP request; supported alteration payloads are unchanged. This does not add server support for view types or change server HTTP-error classification. Three new native Rust cases reproduce the panic on unchanged production and return InvalidInput with the fix. The existing supported-type cases remain passing. Validation at head 88184ea, Linux/Rust 1.97.0, locked offline dependencies: - cargo test --offline --profile ci --locked -p lancedb --no-default-features --features remote --lib remote::table::tests:: (269 passed) - cargo clippy --offline --profile ci --locked -p lancedb --no-default-features --features remote --lib --tests -- -D warnings - cargo fmt --all -- --check Current main merges cleanly; the intervening remote-table change is unrelated nprobes validation. Live Cloud, rebuilt Python/Node bindings and full-workspace integration were not tested. AI-assisted; native regression and before/after results independently checked. --- rust/lancedb/src/remote/table.rs | 37 +++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/rust/lancedb/src/remote/table.rs b/rust/lancedb/src/remote/table.rs index c3b6e7b73..9e2641c22 100644 --- a/rust/lancedb/src/remote/table.rs +++ b/rust/lancedb/src/remote/table.rs @@ -3736,16 +3736,25 @@ impl BaseTable for RemoteTable { value["rename"] = serde_json::Value::String(rename.clone()); } if let Some(data_type) = &alteration.data_type { - let json_data_type = JsonDataType::try_from(data_type).unwrap(); - let json_data_type = serde_json::to_value(&json_data_type).unwrap(); - value["data_type"] = json_data_type; + let json_data_type = + JsonDataType::try_from(data_type).map_err(|err| Error::InvalidInput { + message: format!( + "Cannot serialize data type for column '{}': {err}", + alteration.path + ), + })?; + value["data_type"] = serde_json::to_value(&json_data_type).map_err(|err| { + Error::InvalidInput { + message: format!("Cannot serialize column alteration: {err}"), + } + })?; } if let Some(nullable) = &alteration.nullable { value["nullable"] = serde_json::Value::Bool(*nullable); } - value + Ok(value) }) - .collect::>(); + .collect::>>()?; let mut body = serde_json::json!({ "alterations": body }); self.apply_branch_body(&mut body); let request = self @@ -5067,6 +5076,24 @@ mod tests { } } + #[rstest] + #[case::string_view(DataType::Utf8View)] + #[case::binary_view(DataType::BinaryView)] + #[case::nested_view(DataType::Struct(vec![Field::new("value", DataType::Utf8View, true)].into()))] + #[tokio::test] + async fn test_alter_columns_unsupported_type(#[case] data_type: DataType) { + let table = Table::new_with_handler("my_table", |_| -> http::Response { + panic!("an unserializable alteration must not reach the server") + }); + let err = table + .alter_columns(&[ColumnAlteration::new("value".into()).cast_to(data_type)]) + .await + .unwrap_err(); + assert!(matches!(err, Error::InvalidInput { .. }), "got {err:?}"); + assert!(err.to_string().contains("value")); + assert!(err.to_string().contains("Unsupported type")); + } + #[rstest] #[case(true)] #[case(false)]