use crate::checks::Check;
use crate::violation::Violation;
use sqlparser::ast::{AlterTableOperation, Statement};
pub struct RenameColumnCheck;
impl Check for RenameColumnCheck {
fn check(&self, stmt: &Statement) -> Vec<Violation> {
let Statement::AlterTable {
name, operations, ..
} = stmt
else {
return vec![];
};
let table_name = name.to_string();
operations
.iter()
.filter_map(|op| {
let AlterTableOperation::RenameColumn {
old_column_name,
new_column_name,
} = op
else {
return None;
};
let old_name = old_column_name.to_string();
let new_name = new_column_name.to_string();
Some(Violation::new(
"RENAME COLUMN",
format!(
"Renaming column '{old}' to '{new}' in table '{table}' will cause immediate errors in running application instances. \
Any code referencing the old column name will fail after the rename is applied, causing downtime.",
old = old_name,
new = new_name,
table = table_name
),
format!(
r#"1. Add a new column with the desired name (allows NULL initially):
ALTER TABLE {table} ADD COLUMN {new} <data_type>;
2. Backfill the new column with data from the old column:
UPDATE {table} SET {new} = {old};
3. Add NOT NULL constraint if needed (after backfill):
ALTER TABLE {table} ALTER COLUMN {new} SET NOT NULL;
4. Update your application code to reference the new column name.
5. Deploy the updated application code.
6. Drop the old column in a subsequent migration:
ALTER TABLE {table} DROP COLUMN {old};
This approach maintains compatibility with running instances during the transition."#,
table = table_name,
old = old_name,
new = new_name
),
))
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{assert_allows, assert_detects_violation};
#[test]
fn test_detects_rename_column() {
assert_detects_violation!(
RenameColumnCheck,
"ALTER TABLE users RENAME COLUMN email TO email_address;",
"RENAME COLUMN"
);
}
#[test]
fn test_detects_rename_column_with_schema() {
assert_detects_violation!(
RenameColumnCheck,
"ALTER TABLE public.users RENAME COLUMN old_name TO new_name;",
"RENAME COLUMN"
);
}
#[test]
fn test_ignores_other_alter_operations() {
assert_allows!(
RenameColumnCheck,
"ALTER TABLE users ADD COLUMN email VARCHAR(255);"
);
}
#[test]
fn test_ignores_rename_table() {
assert_allows!(RenameColumnCheck, "ALTER TABLE users RENAME TO customers;");
}
#[test]
fn test_ignores_other_statements() {
assert_allows!(
RenameColumnCheck,
"CREATE TABLE users (id SERIAL PRIMARY KEY);"
);
}
}