on main help sign in
Safely remove indices when removing column
commit4e0e112eb23f858ebdfc1e025157103eddda8368 authorfabianlindfors <fabian@flapplabs.se> dateThu 20 Jan 2022, 19:31 (5 years ago) parent16f6418
3 files changed+80−1
README.md+2 src/migrations/remove_column.rs+14−1 tests/remove_column.rs+64
README.md+2
@@ -364,6 +364,8 @@ column = "created_at"
364364
365365The `remove_column` action will remove an existing column from a table. You can optionally provide a `down` setting. This should be an SQL expression which will be used to determine values for the old schema when inserting or updating rows using the new schema. The `down` setting must be provided when the removed column is `NOT NULL` or doesn't have a default value.
366366
367+Any indices that cover the column will be removed.
368+
367369*Example: remove column `name` from table `users`*
368370
369371```toml
src/migrations/remove_column.rs+14−1
@@ -1,4 +1,4 @@
1−use super::{Action, MigrationContext};
1+use super::{common, Action, MigrationContext};
22use crate::{
33 db::{Conn, Transaction},
44 schema::Schema,
@@ -92,6 +92,19 @@ impl Action for RemoveColumn {
9292 ctx: &MigrationContext,
9393 db: &'a mut dyn Conn,
9494 ) -> anyhow::Result<Option<Transaction<'a>>> {
95+ let indices = common::get_indices_for_column(db, &self.table, &self.column)
96+ .context("failed getting column indices")?;
97+
98+ for index in indices {
99+ db.run(&format!(
100+ "
101+ DROP INDEX CONCURRENTLY IF EXISTS {name}
102+ ",
103+ name = index.name,
104+ ))
105+ .context("failed to drop index")?;
106+ }
107+
95108 // Remove column, function and trigger
96109 let query = format!(
97110 r#"
tests/remove_column.rs+64
@@ -65,3 +65,67 @@ fn remove_column() {
6565
6666 test.run();
6767}
68+
69+#[test]
70+fn remove_column_with_index() {
71+ let mut test = Test::new("Remove column");
72+
73+ test.first_migration(
74+ r#"
75+ name = "create_user_table"
76+
77+ [[actions]]
78+ type = "create_table"
79+ name = "users"
80+ primary_key = ["id"]
81+
82+ [[actions.columns]]
83+ name = "id"
84+ type = "INTEGER"
85+
86+ [[actions.columns]]
87+ name = "name"
88+ type = "TEXT"
89+
90+ [[actions]]
91+ type = "add_index"
92+ table = "users"
93+
94+ [actions.index]
95+ name = "name_idx"
96+ columns = ["name"]
97+ "#,
98+ );
99+
100+ test.second_migration(
101+ r#"
102+ name = "remove_name_column"
103+
104+ [[actions]]
105+ type = "remove_column"
106+ table = "users"
107+ column = "name"
108+ down = "'TEST_DOWN_VALUE'"
109+ "#,
110+ );
111+
112+ test.after_completion(|db| {
113+ // Ensure index has been removed after the migration is complete
114+ let count: i64 = db
115+ .query(
116+ "
117+ SELECT COUNT(*)
118+ FROM pg_catalog.pg_index
119+ JOIN pg_catalog.pg_class ON pg_index.indexrelid = pg_class.oid
120+ WHERE pg_class.relname = 'name_idx'
121+ ",
122+ &[],
123+ )
124+ .unwrap()
125+ .first()
126+ .map(|row| row.get(0))
127+ .unwrap();
128+
129+ assert_eq!(0, count, "expected index to not exist");
130+ });
131+}