aliases are normalized and used in normalized state, but nonnormalized aliases contain everything

This commit is contained in:
Filipriec
2026-08-25 10:22:27 +02:00
parent 2e1b213137
commit e6840f6d9b
19 changed files with 198 additions and 34 deletions

View File

@@ -22,6 +22,7 @@ serde_json = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }
tonic = "0.14.6"
tonic-prost = "0.14.6"
common = { path = "../common" }
[dev-dependencies]
tower = { version = "0.5.3", features = ["util"] }

View File

@@ -853,6 +853,8 @@ error-identifier-underscore = { $label } nesmí začínat podtržítkem.
error-identifier-number = { $label } nesmí začínat číslicí.
error-identifier-too-long = { $label } nesmí být delší než { $limit } znaků.
error-identifier-charset = { $label } může obsahovat jen malá písmena, číslice a podtržítko.
error-alias-charset = { $label } musí obsahovat písmeno, číslo, interpunkci nebo symbol mimo emoji a nesmí obsahovat řídicí znaky.
error-alias-too-long = { $label } nesmí být delší než { $limit } bajtů UTF-8.
error-identifier-reserved = { $label } používá vyhrazený název.
# --- Přidat validaci ----------------------------------------------------------------

View File

@@ -838,6 +838,8 @@ error-identifier-underscore = { $label } cannot start with an underscore.
error-identifier-number = { $label } cannot start with a number.
error-identifier-too-long = { $label } cannot be longer than { $limit } characters.
error-identifier-charset = { $label } may only use lowercase letters, digits and underscores.
error-alias-charset = { $label } must contain a letter, number, punctuation mark or non-emoji symbol, and cannot contain control characters.
error-alias-too-long = { $label } cannot be longer than { $limit } UTF-8 bytes.
error-identifier-reserved = { $label } uses a reserved name.
# --- Add validation --------------------------------------------------------

View File

@@ -851,6 +851,8 @@ error-identifier-underscore = { $label } nesmie začínať podčiarkovníkom.
error-identifier-number = { $label } nesmie začínať číslom.
error-identifier-too-long = { $label } nesmie byť dlhšie ako { $limit } znakov.
error-identifier-charset = { $label } môže obsahovať len malé písmená, číslice a podčiarkovník.
error-alias-charset = { $label } musí obsahovať písmeno, číslo, interpunkciu alebo symbol mimo emoji a nesmie obsahovať riadiace znaky.
error-alias-too-long = { $label } nesmie byť dlhšie ako { $limit } bajtov UTF-8.
error-identifier-reserved = { $label } používa vyhradený názov.
# --- Pridať validáciu ----------------------------------------------------------

View File

@@ -13,7 +13,7 @@
use crate::{
definitions::table_definition::{GeneratedColumnAlias, PostTableDefinitionRequest},
schema::{ColumnCatalog, ColumnDraft, proto_columns, validate_identifier, validate_table_name},
schema::{ColumnCatalog, ColumnDraft, proto_columns, validate_column_alias, validate_identifier, validate_table_name},
{i18n::Locale, tr},
};
@@ -361,10 +361,16 @@ impl TableDraft {
);
for (source, alias) in &renames {
if let Some(error) = validate_identifier(locale, alias, "label-column-alias", true) {
if let Some(error) = validate_column_alias(locale, alias) {
return Err(error);
}
if taken.iter().filter(|name| *name == alias).count() > 1 {
let canonical = common::alias::canonical_alias(alias);
if taken
.iter()
.filter(|name| common::alias::canonical_alias(name) == canonical)
.count()
> 1
{
return Err(tr!(
locale,
"draft-err-alias-taken",
@@ -756,12 +762,9 @@ mod tests {
source: "debit".to_string(),
alias: "Md".to_string(),
}];
assert!(
draft.validate(crate::i18n::Locale::default()).is_err(),
"an alias is a column name"
);
assert!(draft.validate(crate::i18n::Locale::default()).is_ok());
draft.generated_aliases[0].alias = "note".to_string();
draft.generated_aliases[0].alias = "NOTE🙂".to_string();
assert!(
draft.validate(crate::i18n::Locale::default()).is_err(),
"a declared column holds the name"

View File

@@ -27,7 +27,7 @@ use crate::{
PutTableDefinitionRequest,
},
{i18n::Locale, tr},
schema::{ColumnForm, proto_columns, validate_table_name},
schema::{ColumnForm, proto_columns, validate_column_alias, validate_table_name},
services::{authenticated_request, reject_cross_site},
};
@@ -445,9 +445,8 @@ pub(crate) async fn set_column_alias(
table: form.table.clone(),
});
let alias = form.alias.trim().to_string();
if alias.is_empty() {
let message = tr!(Locale::from_headers(&headers), "td-err-choose-rename");
let alias = form.alias;
if let Some(message) = validate_column_alias(Locale::from_headers(&headers), &alias) {
return refuse(state, headers, inputs, Page::Presentation, message).await;
}

View File

@@ -1138,6 +1138,38 @@ pub(crate) fn validate_identifier(
None
}
pub(crate) fn validate_column_alias(
locale: crate::i18n::Locale,
value: &str,
) -> Option<String> {
let label = crate::tr!(locale, "label-column-alias");
if value.is_empty() {
return Some(crate::tr!(locale, "error-identifier-empty", "label" => label));
}
if value != value.trim() {
return Some(crate::tr!(locale, "error-identifier-whitespace", "label" => label));
}
if value.len() > common::alias::MAX_ALIAS_BYTES {
return Some(crate::tr!(
locale,
"error-alias-too-long",
"label" => label,
"limit" => common::alias::MAX_ALIAS_BYTES as i64,
));
}
if common::alias::has_disallowed_alias_characters(value)
|| value.chars().any(|character| matches!(character, '\u{2028}' | '\u{2029}'))
|| common::alias::canonical_alias(value).is_empty()
{
return Some(crate::tr!(locale, "error-alias-charset", "label" => label));
}
let canonical = common::alias::canonical_alias(value);
if canonical != "account" && crate::system_column::is_system_column(&canonical) {
return Some(crate::tr!(locale, "error-identifier-reserved", "label" => label));
}
None
}
/// Postgres's `NAMEDATALEN - 1`: a longer identifier is truncated, not refused.
const MAX_IDENTIFIER_LENGTH: usize = 63;