on main help sign in
Use temporary column for add column migration

Previously the column was added with its final name directly. This could cause trouble if there was a column with the same name removed during the same migration.

commitc54190bfc104d33e4a4351ee3e5eebc9bab721ac authorfabianlindfors <fabian@flapplabs.se> dateFri 31 Dec 2021, 17:20 (5 years ago) parent40ba58b
1 file changed+37−9
src/migrations/add_column.rs+37−9
src/migrations/add_column.rs+37−9
@@ -10,6 +10,15 @@ pub struct AddColumn {
1010}
1111
1212impl AddColumn {
13+ fn temp_column_name(&self, ctx: &Context) -> String {
14+ format!(
15+ "{}_temp_column_{}_{}",
16+ ctx.prefix(),
17+ self.table,
18+ self.column.name,
19+ )
20+ }
21+
1322 fn trigger_name(&self, ctx: &Context) -> String {
1423 format!(
1524 "{}_add_column_{}_{}",
@@ -40,9 +49,10 @@ impl Action for AddColumn {
4049
4150 fn run(&self, ctx: &Context, db: &mut dyn Conn, schema: &Schema) -> anyhow::Result<()> {
4251 let table = schema.get_table(db, &self.table)?;
52+ let temp_column_name = self.temp_column_name(ctx);
4353
4454 let mut definition_parts = vec![
45− self.column.name.to_string(),
55+ temp_column_name.to_string(),
4656 self.column.data_type.to_string(),
4757 ];
4858
@@ -93,7 +103,7 @@ impl Action for AddColumn {
93103 DECLARE
94104 {declarations}
95105 BEGIN
96− NEW.{column_name} = {up};
106+ NEW.{temp_column_name} = {up};
97107 END;
98108 END IF;
99109 RETURN NEW;
@@ -103,7 +113,7 @@ impl Action for AddColumn {
103113 DROP TRIGGER IF EXISTS {trigger_name} ON {table};
104114 CREATE TRIGGER {trigger_name} BEFORE UPDATE OR INSERT ON {table} FOR EACH ROW EXECUTE PROCEDURE {trigger_name}();
105115 ",
106− column_name = self.column.name,
116+ temp_column_name = temp_column_name,
107117 trigger_name = self.trigger_name(ctx),
108118 up = up,
109119 table = self.table,
@@ -114,7 +124,7 @@ impl Action for AddColumn {
114124
115125 // Backfill values in batches
116126 if self.up.is_some() {
117− common::batch_touch_rows(db, &table.real_name, &self.column.name)?;
127+ common::batch_touch_rows(db, &table.real_name, &temp_column_name)?;
118128 }
119129
120130 // Add a temporary NOT NULL constraint if the column shouldn't be nullable.
@@ -130,7 +140,7 @@ impl Action for AddColumn {
130140 ",
131141 table = self.table,
132142 constraint_name = self.not_null_constraint_name(&ctx),
133− column = self.column.name,
143+ column = temp_column_name,
134144 );
135145 db.run(&query)?;
136146 }
@@ -138,7 +148,9 @@ impl Action for AddColumn {
138148 Ok(())
139149 }
140150
141− fn complete(&self, ctx: &Context, db: &mut dyn Conn, _schema: &Schema) -> anyhow::Result<()> {
151+ fn complete(&self, ctx: &Context, db: &mut dyn Conn, schema: &Schema) -> anyhow::Result<()> {
152+ let table = schema.get_table(db, &self.table)?;
153+
142154 // Remove triggers and procedures
143155 let query = format!(
144156 "
@@ -174,7 +186,7 @@ impl Action for AddColumn {
174186 ALTER COLUMN {column} SET NOT NULL
175187 ",
176188 table = self.table,
177− column = self.column.name,
189+ column = self.temp_column_name(ctx),
178190 );
179191 db.run(&query)?;
180192
@@ -190,10 +202,26 @@ impl Action for AddColumn {
190202 db.run(&query)?;
191203 }
192204
205+ // Rename the temporary column to its real name
206+ db.run(&format!(
207+ "
208+ ALTER TABLE {table}
209+ RENAME COLUMN {temp_column_name} TO {column_name}
210+ ",
211+ table = table.real_name,
212+ temp_column_name = self.temp_column_name(ctx),
213+ column_name = self.column.name,
214+ ))?;
215+
193216 Ok(())
194217 }
195218
196− fn update_schema(&self, _ctx: &Context, _schema: &mut Schema) -> anyhow::Result<()> {
219+ fn update_schema(&self, ctx: &Context, schema: &mut Schema) -> anyhow::Result<()> {
220+ schema.change_table(&self.table, |table_changes| {
221+ table_changes.change_column(&self.column.name, |column_changes| {
222+ column_changes.set_column(&self.temp_column_name(ctx));
223+ })
224+ });
197225 Ok(())
198226 }
199227
@@ -216,7 +244,7 @@ impl Action for AddColumn {
216244 DROP COLUMN IF EXISTS {column}
217245 ",
218246 table = self.table,
219− column = self.column.name,
247+ column = self.temp_column_name(ctx),
220248 );
221249 db.run(&query)?;
222250