use crate::checks::pg_helpers::{NodeEnum, ObjectType, range_var_name};
use crate::checks::{Check, Config, MigrationContext};
use crate::violation::Violation;
pub struct RenameColumnCheck;
impl Check for RenameColumnCheck {
fn check(&self, node: &NodeEnum, _config: &Config, _ctx: &MigrationContext) -> Vec<Violation> {
let NodeEnum::RenameStmt(rename) = node else {
return vec![];
};
if rename.rename_type != ObjectType::ObjectColumn as i32 {
return vec![];
}
let table_name = rename
.relation
.as_ref()
.map(range_var_name)
.unwrap_or_default();
let old_name = &rename.subname;
let new_name = &rename.newname;
vec![Violation::new(
"RENAME COLUMN",
format!(
"Renaming column '{old_name}' to '{new_name}' in table '{table_name}' will cause immediate errors in running application instances. \
Any code referencing the old column name will fail after the rename is applied, causing downtime."
),
format!(
r"1. Add a new column with the desired name (allows NULL initially):
ALTER TABLE {table_name} ADD COLUMN {new_name} <data_type>;
2. Backfill the new column with data from the old column:
UPDATE {table_name} SET {new_name} = {old_name};
3. Add NOT NULL constraint if needed (after backfill):
ALTER TABLE {table_name} ALTER COLUMN {new_name} 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_name} DROP COLUMN {old_name};
This approach maintains compatibility with running instances during the transition."
),
)]
}
}
#[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);"
);
}
}