@@ -1,66 +1,45 @@
1−use reshape::migrations::{AlterColumn, Column, ColumnChanges, CreateTable, Migration};
7fn alter_column_data() {
5fn alter_column_data() {
8− let (mut reshape, mut old_db, mut new_db) = common::setup();
6+ let mut test = Test::new("Alter column");
10− let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
8+ test.first_migration(
11− name: "users".to_string(),
9+ r#"
12− primary_key: vec!["id".to_string()],
10+ name = "create_user_table"
13− foreign_keys: vec![],
16− name: "id".to_string(),
17− data_type: "INTEGER".to_string(),
23− name: "name".to_string(),
24− data_type: "TEXT".to_string(),
31− let uppercase_name = Migration::new("uppercase_name", None).with_action(AlterColumn {
32− table: "users".to_string(),
33− column: "name".to_string(),
34− up: Some("UPPER(name)".to_string()),
35− down: Some("LOWER(name)".to_string()),
36− changes: ColumnChanges {
43− let first_migrations = vec![create_users_table.clone()];
12+ [[actions]]
44− let second_migrations = vec![create_users_table.clone(), uppercase_name.clone()];
13+ type = "create_table"
46− // Run first migration, should automatically finish
17+ [[actions.columns]]
47− reshape.migrate(first_migrations.clone()).unwrap();
18+ name = "id"
48− assert!(matches!(reshape.state.status, Status::Idle));
19+ type = "INTEGER"
50− Some(&create_users_table.name),
21+ [[actions.columns]]
51− reshape.state.current_migration.as_ref()
22+ name = "name"
54− // Update search paths
27+ test.second_migration(
56− .simple_query(&reshape::schema_query_for_migration(
29+ name = "uppercase_name"
57− &first_migrations.last().unwrap().name,
30+
59− .unwrap();
32+ type = "alter_column"
40+ test.after_first(|db| {
61− // Insert some test users
41+ // Insert some test users
63− .simple_query(
42+ db.simple_query(
65 INSERT INTO users (id, name) VALUES
44 INSERT INTO users (id, name) VALUES
66 (1, 'john Doe'),
45 (1, 'john Doe'),
@@ -68,103 +47,81 @@ fn alter_column_data() {
70 .unwrap();
49 .unwrap();
72− // Run second migration
52+ test.intermediate(|old_db, new_db| {
73− reshape.migrate(second_migrations.clone()).unwrap();
75− .simple_query(&reshape::schema_query_for_migration(
76− &second_migrations.last().unwrap().name,
80− // Check that the existing users has the altered data
53+ // Check that the existing users has the altered data
81− let expected = vec!["JOHN DOE", "JANE DOE"];
54+ let expected = vec!["JOHN DOE", "JANE DOE"];
82− assert!(new_db
55+ assert!(new_db
83− .query("SELECT name FROM users ORDER BY id", &[],)
56+ .query("SELECT name FROM users ORDER BY id", &[],)
84− .unwrap()
57+ .unwrap()
86− .map(|row| row.get::<_, String>("name"))
59+ .map(|row| row.get::<_, String>("name"))
87− .eq(expected));
60+ .eq(expected));
89− // Insert data using old schema and make sure the new schema gets correct values
62+ // Insert data using old schema and make sure the new schema gets correct values
91− .simple_query("INSERT INTO users (id, name) VALUES (3, 'test testsson')")
64+ .simple_query("INSERT INTO users (id, name) VALUES (3, 'test testsson')")
92− .unwrap();
65+ .unwrap();
93− let result = new_db
66+ let result = new_db
94− .query_one("SELECT name from users WHERE id = 3", &[])
67+ .query_one("SELECT name from users WHERE id = 3", &[])
95− .unwrap();
68+ .unwrap();
96− assert_eq!("TEST TESTSSON", result.get::<_, &str>("name"));
69+ assert_eq!("TEST TESTSSON", result.get::<_, &str>("name"));
98− // Insert data using new schema and make sure the old schema gets correct values
71+ // Insert data using new schema and make sure the old schema gets correct values
100− .simple_query("INSERT INTO users (id, name) VALUES (4, 'TEST TESTSSON')")
73+ .simple_query("INSERT INTO users (id, name) VALUES (4, 'TEST TESTSSON')")
101− .unwrap();
74+ .unwrap();
102− let result = old_db
75+ let result = old_db
103− .query_one("SELECT name from users WHERE id = 4", &[])
76+ .query_one("SELECT name from users WHERE id = 4", &[])
104− .unwrap();
77+ .unwrap();
105− assert_eq!("test testsson", result.get::<_, &str>("name"));
78+ assert_eq!("test testsson", result.get::<_, &str>("name"));
107− reshape.complete_migration().unwrap();
81+ test.run();
111fn alter_column_set_not_null() {
85fn alter_column_set_not_null() {
112− let (mut reshape, mut old_db, mut new_db) = common::setup();
86+ let mut test = Test::new("Set column not null");
114− let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
88+ test.first_migration(
115− name: "users".to_string(),
89+ r#"
116− primary_key: vec!["id".to_string()],
90+ name = "create_user_table"
117− foreign_keys: vec![],
120− name: "id".to_string(),
121− data_type: "INTEGER".to_string(),
127− name: "name".to_string(),
128− data_type: "TEXT".to_string(),
135− let set_name_not_null = Migration::new("set_name_not_null", None).with_action(AlterColumn {
136− table: "users".to_string(),
137− column: "name".to_string(),
138− up: Some("COALESCE(name, 'TEST_DEFAULT_VALUE')".to_string()),
139− down: Some("name".to_string()),
140− changes: ColumnChanges {
142− nullable: Some(false),
147− let first_migrations = vec![create_users_table.clone()];
92+ [[actions]]
148− let second_migrations = vec![create_users_table.clone(), set_name_not_null.clone()];
93+ type = "create_table"
150− // Run first migration, should automatically finish
97+ [[actions.columns]]
151− reshape.migrate(first_migrations.clone()).unwrap();
98+ name = "id"
152− assert!(matches!(reshape.state.status, Status::Idle));
99+ type = "INTEGER"
154− Some(&create_users_table.name),
101+ [[actions.columns]]
155− reshape.state.current_migration.as_ref()
102+ name = "name"
158− // Update search paths
107+ test.second_migration(
160− .simple_query(&reshape::schema_query_for_migration(
109+ name = "set_name_not_null"
161− &first_migrations.last().unwrap().name,
112+ type = "alter_column"
115+ up = "COALESCE(name, 'TEST_DEFAULT_VALUE')"
122+ test.after_first(|db| {
165− // Insert some test users
123+ // Insert some test users
167− .simple_query(
124+ db.simple_query(
169 INSERT INTO users (id, name) VALUES
126 INSERT INTO users (id, name) VALUES
170 (1, 'John Doe'),
127 (1, 'John Doe'),
@@ -172,104 +129,180 @@ fn alter_column_set_not_null() {
174 .unwrap();
131 .unwrap();
176− // Run second migration
134+ test.intermediate(|old_db, new_db| {
177− reshape.migrate(second_migrations.clone()).unwrap();
135+ // Check that existing users got the correct values
136+ let expected = vec!["John Doe", "TEST_DEFAULT_VALUE"];
138+ .query("SELECT name FROM users ORDER BY id", &[],)
141+ .map(|row| row.get::<_, String>("name"))
144+ // Insert data using old schema and make sure the new schema gets correct values
146+ .simple_query("INSERT INTO users (id, name) VALUES (3, NULL)")
178− new_db
148+ let result = new_db
179− .simple_query(&reshape::schema_query_for_migration(
149+ .query_one("SELECT name from users WHERE id = 3", &[])
180− &second_migrations.last().unwrap().name,
150+ .unwrap();
181− ))
151+ assert_eq!("TEST_DEFAULT_VALUE", result.get::<_, &str>("name"));
153+ // Insert data using new schema and make sure the old schema gets correct values
155+ .simple_query("INSERT INTO users (id, name) VALUES (4, 'Jane Doe')")
158+ .query_one("SELECT name from users WHERE id = 4", &[])
160+ assert_eq!("Jane Doe", result.get::<_, &str>("name"));
162+ // Ensure NULL can't be inserted using the new schema
163+ let result = new_db.simple_query("INSERT INTO users (id, name) VALUES (5, NULL)");
164+ assert!(result.is_err(), "expected insert to fail");
184− // Check that existing users got the correct values
167+ test.after_completion(|db| {
185− let expected = vec!["John Doe", "TEST_DEFAULT_VALUE"];
168+ // Ensure NULL can't be inserted
187− .query("SELECT name FROM users ORDER BY id", &[],)
190− .map(|row| row.get::<_, String>("name"))
193− // Insert data using old schema and make sure the new schema gets correct values
195− .simple_query("INSERT INTO users (id, name) VALUES (3, NULL)")
169+ let result = db.simple_query("INSERT INTO users (id, name) VALUES (5, NULL)");
196− .unwrap();
170+ assert!(result.is_err(), "expected insert to fail");
197− let result = new_db
171+ });
198− .query_one("SELECT name from users WHERE id = 3", &[])
200− assert_eq!("TEST_DEFAULT_VALUE", result.get::<_, &str>("name"));
202− // Insert data using new schema and make sure the old schema gets correct values
173+ test.after_abort(|db| {
203− new_db
174+ // Ensure NULL can be inserted
175+ let result = db.simple_query("INSERT INTO users (id, name) VALUES (5, NULL)");
176+ assert!(result.is_ok(), "expected insert to succeed");
183+fn alter_column_set_nullable() {
184+ let mut test = Test::new("Set column nullable");
186+ test.first_migration(
188+ name = "create_user_table"
191+ type = "create_table"
193+ primary_key = ["id"]
206+ test.second_migration(
208+ name = "set_name_nullable"
211+ type = "alter_column"
214+ down = "COALESCE(name, 'TEST_DEFAULT_VALUE')"
221+ test.after_first(|db| {
222+ // Insert a test user
204− .simple_query("INSERT INTO users (id, name) VALUES (4, 'Jane Doe')")
225+ INSERT INTO users (id, name) VALUES
205− .unwrap();
226+ (1, 'John Doe')
206− let result = old_db
227+ ",
207− .query_one("SELECT name from users WHERE id = 4", &[])
228+ )
208 .unwrap();
229 .unwrap();
232+ test.intermediate(|old_db, new_db| {
233+ // Insert data using new schema and make sure the old schema gets correct values
235+ .simple_query("INSERT INTO users (id, name) VALUES (2, NULL)")
238+ .query_one("SELECT name from users WHERE id = 2", &[])
209− assert_eq!("Jane Doe", result.get::<_, &str>("name"));
240+ assert_eq!("TEST_DEFAULT_VALUE", result.get::<_, &str>("name"));
242+ // Ensure NULL can't be inserted using the old schema
243+ let result = old_db.simple_query("INSERT INTO users (id, name) VALUES (3, NULL)");
244+ assert!(result.is_err(), "expected insert to fail");
246+ // Ensure NULL can be inserted using the new schema
247+ let result = new_db.simple_query("INSERT INTO users (id, name) VALUES (4, NULL)");
248+ assert!(result.is_ok(), "expected insert to succeed");
251+ test.after_completion(|db| {
252+ // Ensure NULL can be inserted
253+ let result = db.simple_query("INSERT INTO users (id, name) VALUES (5, NULL)");
254+ assert!(result.is_ok(), "expected insert to succeed");
257+ test.after_abort(|db| {
258+ // Ensure NULL can't be inserted
259+ let result = db.simple_query("INSERT INTO users (id, name) VALUES (5, NULL)");
260+ assert!(result.is_err(), "expected insert to fail");
211− reshape.complete_migration().unwrap();
263+ test.run();
215fn alter_column_rename() {
267fn alter_column_rename() {
216− let (mut reshape, mut old_db, mut new_db) = common::setup();
268+ let mut test = Test::new("Rename column");
218− let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
270+ test.first_migration(
272+ name = "create_user_table"
275+ type = "create_table"
219− name: "users".to_string(),
276+ name = "users"
220− primary_key: vec!["id".to_string()],
277+ primary_key = ["id"]
221− foreign_keys: vec![],
278+
222− columns: vec![
279+ [[actions.columns]]
223− Column {
280+ name = "id"
224− name: "id".to_string(),
281+ type = "INTEGER"
225− data_type: "INTEGER".to_string(),
282+
226− nullable: true,
283+ [[actions.columns]]
231− name: "name".to_string(),
284+ name = "name"
232− data_type: "TEXT".to_string(),
285+ type = "TEXT"
239− let rename_to_full_name =
240− Migration::new("rename_to_full_name", None).with_action(AlterColumn {
241− table: "users".to_string(),
242− column: "name".to_string(),
243− up: None, // up and down are not required when only renaming a column
245− changes: ColumnChanges {
248− name: Some("full_name".to_string()),
252− let first_migrations = vec![create_users_table.clone()];
253− let second_migrations = vec![create_users_table.clone(), rename_to_full_name.clone()];
255− // Run first migration, should automatically finish
256− reshape.migrate(first_migrations.clone()).unwrap();
257− assert!(matches!(reshape.state.status, Status::Idle));
259− Some(&create_users_table.name),
260− reshape.state.current_migration.as_ref()
263− // Update search paths
289+ test.second_migration(
265− .simple_query(&reshape::schema_query_for_migration(
291+ name = "set_name_not_null"
266− &first_migrations.last().unwrap().name,
294+ type = "alter_column"
303+ test.after_first(|db| {
270− // Insert some test data
304+ // Insert some test data
272− .simple_query(
305+ db.simple_query(
274 INSERT INTO users (id, name) VALUES
307 INSERT INTO users (id, name) VALUES
275 (1, 'John Doe'),
308 (1, 'John Doe'),
@@ -277,97 +310,69 @@ fn alter_column_rename() {
279 .unwrap();
312 .unwrap();
281− // Run second migration
282− reshape.migrate(second_migrations.clone()).unwrap();
283− new_db
315+ test.intermediate(|_, new_db| {
284− .simple_query(&reshape::schema_query_for_migration(
285− &second_migrations.last().unwrap().name,
289− // Check that existing values can be queried using new column name
316+ // Check that existing values can be queried using new column name
290− let expected = vec!["John Doe", "Jane Doe"];
317+ let expected = vec!["John Doe", "Jane Doe"];
291− assert!(new_db
318+ assert!(new_db
292− .query("SELECT full_name FROM users ORDER BY id", &[],)
319+ .query("SELECT full_name FROM users ORDER BY id", &[],)
293− .unwrap()
320+ .unwrap()
295− .map(|row| row.get::<_, String>("full_name"))
322+ .map(|row| row.get::<_, String>("full_name"))
296− .eq(expected));
323+ .eq(expected));
298− reshape.complete_migration().unwrap();
326+ test.run();
302fn alter_column_multiple() {
330fn alter_column_multiple() {
303− let (mut reshape, mut old_db, mut new_db) = common::setup();
331+ let mut test = Test::new("Alter column value multiple times");
305− let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
333+ test.first_migration(
335+ name = "create_user_table"
338+ type = "create_table"
306− name: "users".to_string(),
339+ name = "users"
307− primary_key: vec!["id".to_string()],
340+ primary_key = ["id"]
308− foreign_keys: vec![],
341+
309− columns: vec![
342+ [[actions.columns]]
311− name: "id".to_string(),
343+ name = "id"
312− data_type: "INTEGER".to_string(),
344+ type = "INTEGER"
314− default: None,
346+ [[actions.columns]]
318− name: "counter".to_string(),
347+ name = "counter"
319− data_type: "INTEGER".to_string(),
348+ type = "INTEGER"
320− nullable: false,
349+ nullable = false
321− default: None,
350+ "#,
326− let increment_counter_twice = Migration::new("increment_counter_twice", None)
327− .with_action(AlterColumn {
328− table: "users".to_string(),
329− column: "counter".to_string(),
330− up: Some("counter + 1".to_string()),
331− down: Some("counter - 1".to_string()),
332− changes: ColumnChanges {
338− .with_action(AlterColumn {
339− table: "users".to_string(),
340− column: "counter".to_string(),
341− up: Some("counter + 1".to_string()),
342− down: Some("counter - 1".to_string()),
343− changes: ColumnChanges {
350− let first_migrations = vec![create_users_table.clone()];
351− let second_migrations = vec![create_users_table.clone(), increment_counter_twice.clone()];
353− // Run first migration, should automatically finish
354− reshape.migrate(first_migrations.clone()).unwrap();
355− assert!(matches!(reshape.state.status, Status::Idle));
357− Some(&create_users_table.name),
358− reshape.state.current_migration.as_ref()
361− // Update search paths
353+ test.second_migration(
363− .simple_query(&reshape::schema_query_for_migration(
355+ name = "increment_counter_twice"
364− &first_migrations.last().unwrap().name,
356+
366− .unwrap();
358+ type = "alter_column"
362+ down = "counter - 1"
365+ type = "alter_column"
369+ down = "counter - 1"
373+ test.after_first(|db| {
368− // Insert some test data
374+ // Insert some test data
370− .simple_query(
375+ db.simple_query(
372 INSERT INTO users (id, counter) VALUES
377 INSERT INTO users (id, counter) VALUES
@@ -375,22 +380,355 @@ fn alter_column_multiple() {
377 .unwrap();
382 .unwrap();
385+ test.intermediate(|old_db, new_db| {
386+ // Check that the existing data has been updated
387+ let expected = vec![2, 102];
388+ let results: Vec<i32> = new_db
389+ .query("SELECT counter FROM users ORDER BY id", &[])
392+ .map(|row| row.get::<_, i32>("counter"))
394+ assert_eq!(expected, results);
396+ // Update data using old schema and make sure it was updated for the new schema
398+ .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
400+ let result: i32 = new_db
401+ .query("SELECT counter FROM users WHERE id = 1", &[])
404+ .map(|row| row.get("counter"))
407+ assert_eq!(52, result);
409+ // Update data using new schema and make sure it was updated for the old schema
411+ .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
413+ let result: i32 = old_db
414+ .query("SELECT counter FROM users WHERE id = 1", &[])
417+ .map(|row| row.get("counter"))
420+ assert_eq!(48, result);
379− // Run second migration
426+#[test]
380− reshape.migrate(second_migrations.clone()).unwrap();
427+fn alter_column_default() {
381− new_db
428+ let mut test = Test::new("Change default value for column");
430+ test.first_migration(
432+ name = "create_user_table"
435+ type = "create_table"
437+ primary_key = ["id"]
447+ default = "'DEFAULT'"
451+ test.second_migration(
453+ name = "change_name_default"
456+ type = "alter_column"
461+ default = "'NEW DEFAULT'"
465+ test.after_first(|db| {
466+ // Insert a test user
382− .simple_query(&reshape::schema_query_for_migration(
467+ db.simple_query(
383− &second_migrations.last().unwrap().name,
468+ "
469+ INSERT INTO users (id) VALUES (1)
385 .unwrap();
472 .unwrap();
475+ test.intermediate(|old_db, new_db| {
476+ // Check that the existing users has the old default value
477+ let expected = vec!["DEFAULT"];
479+ .query("SELECT name FROM users", &[],)
482+ .map(|row| row.get::<_, String>("name"))
485+ // Insert data using old schema and make those get the old default value
487+ .simple_query("INSERT INTO users (id) VALUES (2)")
490+ .query_one("SELECT name from users WHERE id = 2", &[])
492+ assert_eq!("DEFAULT", result.get::<_, &str>("name"));
494+ // Insert data using new schema and make sure it gets the new default value
496+ .simple_query("INSERT INTO users (id) VALUES (3)")
499+ .query_one("SELECT name from users WHERE id = 3", &[])
501+ assert_eq!("NEW DEFAULT", result.get::<_, &str>("name"));
508+fn alter_column_with_index() {
509+ let mut test = Test::new("Alter column with index");
511+ test.first_migration(
513+ name = "create_user_table"
516+ type = "create_table"
518+ primary_key = ["id"]
537+ name = "users_name_idx"
538+ columns = ["first_name", "last_name"]
542+ test.second_migration(
544+ name = "uppercase_last_name"
547+ type = "alter_column"
549+ column = "last_name"
550+ up = "UPPER(last_name)"
551+ down = "LOWER(last_name)"
555+ test.after_completion(|db| {
556+ // Make sure index still exists
557+ let result: i64 = db
561+ FROM pg_catalog.pg_index
562+ JOIN pg_catalog.pg_class ON pg_index.indexrelid = pg_class.oid
563+ WHERE pg_class.relname = 'users_name_idx'
569+ .map(|row| row.get(0))
571+ assert_eq!(1, result, "expected index to still exist");
578+fn alter_column_with_unique_index() {
579+ let mut test = Test::new("Alter column with unique index");
581+ test.first_migration(
583+ name = "create_user_table"
586+ type = "create_table"
588+ primary_key = ["id"]
609+ test.second_migration(
611+ name = "uppercase_name"
614+ type = "alter_column"
618+ down = "LOWER(name)"
622+ test.after_first(|db| {
623+ db.simple_query("INSERT INTO users (id, name) VALUES (1, 'Test')")
627+ test.intermediate(|old_db, new_db| {
628+ // Try inserting a value which duplicates the uppercase value of an existing row
629+ let result = new_db.simple_query("INSERT INTO users (id, name) VALUES (2, 'TEST')");
632+ "expected duplicate insert to new schema to fail"
635+ // Try inserting a value which duplicates the lowercase value of an existing row
637+ .simple_query("INSERT INTO users (id, name) VALUES (2, 'JOHN')")
639+ let result = old_db.simple_query("INSERT INTO users (id, name) VALUES (3, 'john')");
642+ "expected duplicate insert to old schema to fail"
646+ test.after_completion(|db| {
647+ // Make sure index still exists
648+ let is_unique: bool = db
651+ SELECT pg_index.indisunique
652+ FROM pg_catalog.pg_index
653+ JOIN pg_catalog.pg_class ON pg_index.indexrelid = pg_class.oid
654+ WHERE pg_class.relname = 'name_idx'
660+ .map(|row| row.get("indisunique"))
662+ assert!(is_unique, "expected index to still be unique");
669+fn alter_column_with_hash_index() {
670+ let mut test = Test::new("Alter column with custom index type");
672+ test.first_migration(
674+ name = "create_user_table"
677+ type = "create_table"
679+ primary_key = ["id"]
700+ test.second_migration(
702+ name = "uppercase_name"
705+ type = "alter_column"
709+ down = "LOWER(name)"
713+ test.after_completion(|db| {
714+ // Make sure index still has type GIN
715+ let index_type: String = db
719+ FROM pg_catalog.pg_index
720+ JOIN pg_catalog.pg_class ON pg_index.indexrelid = pg_class.oid
721+ JOIN pg_catalog.pg_am ON pg_class.relam = pg_am.oid
722+ WHERE pg_class.relname = 'name_idx'
728+ .map(|row| row.get("amname"))
730+ assert_eq!("hash", index_type);
387− // Check that the existing data has been updated
733+ test.run();
388− let expected = vec![2, 102];
389− let results: Vec<i32> = new_db
390− .query("SELECT counter FROM users ORDER BY id", &[])
393− .map(|row| row.get::<_, i32>("counter"))
395− assert_eq!(expected, results);