fix: Remove duplicate code in table_migration.rs
All checks were successful
BotServer CI/CD / build (push) Successful in 4m27s

- Removed duplicate DbColumn struct, PROTECTED_COLUMNS const, and sync_table_schema fn
- File now has single clean implementation with column drop protection
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-04-04 11:11:22 -03:00
parent d785d255c6
commit 62e9a64340

View file

@ -172,151 +172,6 @@ pub fn sync_table_schema(
Ok(result)
}
/// Column metadata from database
#[derive(Debug, Clone)]
pub struct DbColumn {
pub name: String,
pub data_type: String,
pub is_nullable: bool,
}
/// Columns that should never be dropped automatically
const PROTECTED_COLUMNS: &[&str] = &[
"id",
"bot_id",
"org_id",
"user_id",
"created_at",
"updated_at",
"deleted_at",
"is_deleted",
"version",
"tenant_id",
];
/// Compare and sync table schema with definition
pub fn sync_table_schema(
table: &TableDefinition,
existing_columns: &[DbColumn],
create_sql: &str,
conn: &mut diesel::PgConnection,
) -> Result<MigrationResult, Box<dyn Error + Send + Sync>> {
let mut result = MigrationResult::default();
// If no columns exist, create the table
if existing_columns.is_empty() {
info!("Creating new table: {}", table.name);
sql_query(create_sql)
.execute(conn)
.map_err(|e| format!("Failed to create table {}: {}", table.name, e))?;
result.tables_created += 1;
return Ok(result);
}
let table_name = sanitize_identifier(&table.name);
let defined_col_names: std::collections::HashSet<String> =
table.fields.iter().map(|f| f.name.clone()).collect();
let existing_col_names: std::collections::HashSet<String> =
existing_columns.iter().map(|c| c.name.clone()).collect();
// Add missing columns
let mut missing_columns: Vec<&FieldDefinition> = Vec::new();
for field in &table.fields {
if !existing_col_names.contains(&field.name) {
missing_columns.push(field);
}
}
if !missing_columns.is_empty() {
info!(
"Table {} is missing {} columns, adding them",
table.name,
missing_columns.len()
);
for field in &missing_columns {
let sql_type = map_type_to_sql(field, "postgres");
let column_sql = format!(
"ALTER TABLE {} ADD COLUMN IF NOT EXISTS {} {}",
&table_name,
sanitize_identifier(&field.name),
sql_type
);
info!(
"Adding column: {}.{} ({})",
table.name, field.name, sql_type
);
match sql_query(&column_sql).execute(conn) {
Ok(_) => {
result.columns_added += 1;
info!("Successfully added column {}.{}", table.name, field.name);
}
Err(e) => {
let err_str = e.to_string();
if !err_str.contains("already exists") && !err_str.contains("duplicate column")
{
let error_msg =
format!("Failed to add column {}.{}: {}", table.name, field.name, e);
error!("{}", error_msg);
result.errors.push(error_msg);
} else {
info!(
"Column {}.{} already exists, skipping",
table.name, field.name
);
}
}
}
}
result.tables_altered += 1;
}
// Drop columns that were removed from definition (with protection)
let mut orphaned_columns: Vec<&DbColumn> = Vec::new();
for col in existing_columns {
if !defined_col_names.contains(&col.name) && !PROTECTED_COLUMNS.contains(&col.name.as_str())
{
orphaned_columns.push(col);
}
}
if !orphaned_columns.is_empty() {
warn!(
"Table {} has {} orphaned columns not in definition:",
table.name,
orphaned_columns.len()
);
for col in &orphaned_columns {
info!("Dropping orphaned column: {}.{}", table.name, col.name);
let drop_sql = format!(
"ALTER TABLE {} DROP COLUMN IF EXISTS {}",
&table_name,
sanitize_identifier(&col.name)
);
match sql_query(&drop_sql).execute(conn) {
Ok(_) => {
result.columns_dropped += 1;
info!("Successfully dropped column {}.{}", table.name, col.name);
}
Err(e) => {
let error_msg =
format!("Failed to drop column {}.{}: {}", table.name, col.name, e);
error!("{}", error_msg);
result.errors.push(error_msg);
}
}
}
if result.columns_dropped > 0 {
result.tables_altered += 1;
}
}
Ok(result)
}
/// Get existing columns from a table
pub fn get_table_columns(
table_name: &str,