fix: Remove duplicate code in table_migration.rs
All checks were successful
BotServer CI/CD / build (push) Successful in 4m27s
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:
parent
d785d255c6
commit
62e9a64340
1 changed files with 0 additions and 145 deletions
|
|
@ -172,151 +172,6 @@ pub fn sync_table_schema(
|
||||||
Ok(result)
|
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
|
/// Get existing columns from a table
|
||||||
pub fn get_table_columns(
|
pub fn get_table_columns(
|
||||||
table_name: &str,
|
table_name: &str,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue