fix: tell a caller their view name is invalid, not their table name

`validate_view_reference` deferred to `validate_table_name`, so a view
name carrying a `$` came back as "Invalid table name" -- an object the
caller never named. Views get the treatment Secrets already have: a
validator worded for the object, in utils beside its siblings.
This commit is contained in:
Jack Ye
2026-09-22 22:09:30 -07:00
parent fdba922b1d
commit ef681c0c30
3 changed files with 30 additions and 13 deletions
+2 -11
View File
@@ -38,7 +38,8 @@ use crate::remote::{
};
use crate::secrets::SecretInfo;
use crate::utils::{
validate_namespace, validate_secret_component, validate_secret_reference, validate_table_name,
validate_namespace, validate_secret_component, validate_secret_reference,
validate_view_reference,
};
use crate::view::ViewDescription;
use lance::io::ObjectStoreParams;
@@ -416,16 +417,6 @@ impl CloneTableBuilder {
}
}
/// Validate a view name and every segment of the namespace path holding it.
///
/// A view is addressed by the two joined into one identifier, so a segment
/// carrying the delimiter would split back apart as a different view. Neither
/// name accepts one, which is what lets the join have one meaning.
fn validate_view_reference(name: &str, namespace_path: &[String]) -> Result<()> {
validate_namespace(namespace_path)?;
validate_table_name(name)
}
/// A connection to LanceDB
#[derive(Clone)]
pub struct Connection {
+7 -2
View File
@@ -4073,9 +4073,14 @@ mod tests {
("", vec![]),
("..", vec![]),
] {
let error = match conn.describe_view(name, &namespace).await {
Ok(_) => panic!("accepted {name:?} in {namespace:?}"),
Err(error) => error.to_string(),
};
// A view is not a table, so the refusal says so.
assert!(
conn.describe_view(name, &namespace).await.is_err(),
"accepted {name:?} in {namespace:?}"
error.contains("view name") || error.contains("view namespace path segment"),
"{error}"
);
}
}
+21
View File
@@ -174,6 +174,27 @@ pub fn validate_secret_reference(name: &str, namespace_path: &[String]) -> Resul
validate_secret_component("Secret name", name)
}
/// Validate a view name and every segment of the namespace path holding it.
///
/// Worded for a view rather than deferring to [`validate_table_name`]: a view
/// is not a table, and a caller who mistypes one should not be told their
/// table name is invalid. The rule is the same one every object name follows,
/// which is what lets the `$`-joined identifier split back apart.
pub fn validate_view_reference(name: &str, namespace_path: &[String]) -> Result<()> {
for segment in namespace_path {
validate_view_component("view namespace path segment", segment)?;
}
validate_view_component("view name", name)
}
/// Validate one component of a view identifier: a view name, or one segment of
/// the namespace path holding it.
pub fn validate_view_component(what: &str, value: &str) -> Result<()> {
check_object_name(value).map_err(|reason| Error::InvalidInput {
message: format!("invalid {what} '{value}': {reason}"),
})
}
/// Validate all components of a namespace
///
/// Iterates through all namespace components and validates each one.