feat(duckdb): support column rename

Closes #9506
This commit is contained in:
t8y2
2026-09-26 16:45:21 +00:00
parent 3c46f8d440
commit 0a4e46a25f
3 changed files with 96 additions and 5 deletions
@@ -1020,6 +1020,20 @@ pub(super) fn build_sqlite_existing_column_sql(
statements
}
pub(super) fn build_duckdb_existing_column_sql(table: &str, column: &EditableStructureColumn) -> Vec<String> {
let Some(original) = &column.original else {
return Vec::new();
};
if column.name == original.name {
return Vec::new();
}
vec![format!(
"ALTER TABLE {table} RENAME COLUMN {} TO {};",
quote_ident(StructureDialect::DuckDb, &original.name),
quote_ident(StructureDialect::DuckDb, &column.name)
)]
}
pub(super) fn build_questdb_existing_column_sql(table: &str, column: &EditableStructureColumn) -> Vec<String> {
let Some(original) = &column.original else {
return Vec::new();
@@ -1,10 +1,11 @@
use super::column_alter::{
build_clickhouse_existing_column_sql, build_dameng_existing_column_sql, build_doris_existing_column_sql,
build_h2_existing_column_sql, build_informix_existing_column_sql, build_iris_existing_column_sql,
build_mysql_existing_column_clause, build_oracle_like_existing_column_sql, build_oscar_existing_column_sql,
build_postgres_existing_column_sql, build_questdb_existing_column_sql, build_sqlite_existing_column_sql,
build_sqlserver_existing_column_sql, build_xugu_existing_column_sql, dameng_drops_identity,
has_column_extra_change, has_existing_column_attribute_change, validate_dameng_existing_identity_change,
build_duckdb_existing_column_sql, build_h2_existing_column_sql, build_informix_existing_column_sql,
build_iris_existing_column_sql, build_mysql_existing_column_clause, build_oracle_like_existing_column_sql,
build_oscar_existing_column_sql, build_postgres_existing_column_sql, build_questdb_existing_column_sql,
build_sqlite_existing_column_sql, build_sqlserver_existing_column_sql, build_xugu_existing_column_sql,
dameng_drops_identity, has_column_extra_change, has_existing_column_attribute_change,
validate_dameng_existing_identity_change,
};
use super::column_format::{
column_definition, has_dameng_identity, is_dameng_identity_compatible_type, is_mysql_character_data_type,
@@ -291,6 +292,7 @@ pub(super) fn build_column_sql(options: &TableStructureSqlOptions, warnings: &mu
warnings,
)),
StructureDialect::Sqlite => statements.extend(build_sqlite_existing_column_sql(&table, column, warnings)),
StructureDialect::DuckDb => statements.extend(build_duckdb_existing_column_sql(&table, column)),
StructureDialect::Questdb => statements.extend(build_questdb_existing_column_sql(&table, column)),
_ => warnings.push(format!("Editing existing columns is not supported for {database_label} yet.")),
}
@@ -4123,6 +4123,81 @@ fn builds_duckdb_create_table_statements() {
);
}
#[cfg(feature = "duckdb-sidecar")]
fn existing_duckdb_column(name: &str) -> EditableStructureColumn {
let mut existing = column(name);
existing.original = Some(ColumnInfo {
name: name.to_string(),
data_type: existing.data_type.clone(),
is_nullable: existing.is_nullable,
column_default: None,
..Default::default()
});
existing
}
#[cfg(feature = "duckdb-sidecar")]
#[test]
fn duckdb_column_rename_generates_quoted_sql() {
let mut renamed = existing_duckdb_column("old_name");
renamed.name = "new_name".to_string();
let result =
build_table_structure_change_sql(structure_change_options(DatabaseType::DuckDb, None, "events", vec![renamed]));
assert!(result.warnings.is_empty(), "{:?}", result.warnings);
assert_eq!(result.statements, vec![r#"ALTER TABLE "events" RENAME COLUMN "old_name" TO "new_name";"#]);
}
#[cfg(feature = "duckdb-sidecar")]
#[test]
fn duckdb_column_rename_quotes_special_identifiers() {
let mut renamed = existing_duckdb_column("old \"code\"");
renamed.name = "select value".to_string();
let result = build_table_structure_change_sql(structure_change_options(
DatabaseType::DuckDb,
None,
"event log",
vec![renamed],
));
assert!(result.warnings.is_empty(), "{:?}", result.warnings);
assert_eq!(result.statements, vec![r#"ALTER TABLE "event log" RENAME COLUMN "old ""code""" TO "select value";"#]);
}
#[cfg(feature = "duckdb-sidecar")]
#[test]
fn duckdb_column_rename_ignores_unchanged_columns() {
let unchanged = existing_duckdb_column("event_id");
let result = build_table_structure_change_sql(structure_change_options(
DatabaseType::DuckDb,
None,
"events",
vec![unchanged],
));
assert!(result.statements.is_empty());
assert!(result.warnings.is_empty());
}
#[cfg(feature = "duckdb-sidecar")]
#[test]
fn duckdb_column_rename_with_unsupported_attribute_change_fails_closed() {
let mut changed = existing_duckdb_column("event_id");
changed.name = "renamed_id".to_string();
changed.data_type = "BIGINT".to_string();
changed.is_nullable = false;
changed.default_value = "42".to_string();
let result =
build_table_structure_change_sql(structure_change_options(DatabaseType::DuckDb, None, "events", vec![changed]));
assert!(result.statements.is_empty());
assert_eq!(result.warnings, vec!["Editing existing columns is not supported for duckdb yet."]);
}
#[test]
fn builds_clickhouse_nullable_comment_and_reorder_statements() {
let mut source = column("source");