@@ -1,66 +1,45 @@
1−use reshape::migrations::{AlterColumn, Column, ColumnChanges, CreateTable, Migration};
2−use reshape::Status;
3−
41mod common;
2+use common::Test;
53
64#[test]
75fn alter_column_data() {
8− let (mut reshape, mut old_db, mut new_db) = common::setup();
9−
10− let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
11− name: "users".to_string(),
12− primary_key: vec!["id".to_string()],
13− foreign_keys: vec![],
14− columns: vec![
15− Column {
16− name: "id".to_string(),
17− data_type: "INTEGER".to_string(),
18− nullable: true,
19− default: None,
20− generated: None,
21− },
22− Column {
23− name: "name".to_string(),
24− data_type: "TEXT".to_string(),
25− nullable: false,
26− default: None,
27− generated: None,
28− },
29− ],
30− });
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 {
37− data_type: None,
38− nullable: None,
39− name: None,
40− },
41− });
6+ let mut test = Test::new("Alter column");
7+
8+ test.first_migration(
9+ r#"
10+ name = "create_user_table"
4211
43− let first_migrations = vec![create_users_table.clone()];
44− let second_migrations = vec![create_users_table.clone(), uppercase_name.clone()];
12+ [[actions]]
13+ type = "create_table"
14+ name = "users"
15+ primary_key = ["id"]
4516
46− // Run first migration, should automatically finish
47− reshape.migrate(first_migrations.clone()).unwrap();
48− assert!(matches!(reshape.state.status, Status::Idle));
49− assert_eq!(
50− Some(&create_users_table.name),
51− reshape.state.current_migration.as_ref()
17+ [[actions.columns]]
18+ name = "id"
19+ type = "INTEGER"
20+
21+ [[actions.columns]]
22+ name = "name"
23+ type = "TEXT"
24+ "#,
5225 );
5326
54− // Update search paths
55− old_db
56− .simple_query(&reshape::schema_query_for_migration(
57− &first_migrations.last().unwrap().name,
58− ))
59− .unwrap();
27+ test.second_migration(
28+ r#"
29+ name = "uppercase_name"
30+
31+ [[actions]]
32+ type = "alter_column"
33+ table = "users"
34+ column = "name"
35+ up = "UPPER(name)"
36+ down = "LOWER(name)"
37+ "#,
38+ );
6039
61− // Insert some test users
62− old_db
63− .simple_query(
40+ test.after_first(|db| {
41+ // Insert some test users
42+ db.simple_query(
6443 "
6544 INSERT INTO users (id, name) VALUES
6645 (1, 'john Doe'),
@@ -68,103 +47,81 @@ fn alter_column_data() {
6847 ",
6948 )
7049 .unwrap();
50+ });
7151
72− // Run second migration
73− reshape.migrate(second_migrations.clone()).unwrap();
74− new_db
75− .simple_query(&reshape::schema_query_for_migration(
76− &second_migrations.last().unwrap().name,
77− ))
78− .unwrap();
79−
80− // Check that the existing users has the altered data
81− let expected = vec!["JOHN DOE", "JANE DOE"];
82− assert!(new_db
83− .query("SELECT name FROM users ORDER BY id", &[],)
84− .unwrap()
85− .iter()
86− .map(|row| row.get::<_, String>("name"))
87− .eq(expected));
88−
89− // Insert data using old schema and make sure the new schema gets correct values
90− old_db
91− .simple_query("INSERT INTO users (id, name) VALUES (3, 'test testsson')")
92− .unwrap();
93− let result = new_db
94− .query_one("SELECT name from users WHERE id = 3", &[])
95− .unwrap();
96− assert_eq!("TEST TESTSSON", result.get::<_, &str>("name"));
97−
98− // Insert data using new schema and make sure the old schema gets correct values
99− new_db
100− .simple_query("INSERT INTO users (id, name) VALUES (4, 'TEST TESTSSON')")
101− .unwrap();
102− let result = old_db
103− .query_one("SELECT name from users WHERE id = 4", &[])
104− .unwrap();
105− assert_eq!("test testsson", result.get::<_, &str>("name"));
52+ test.intermediate(|old_db, new_db| {
53+ // Check that the existing users has the altered data
54+ let expected = vec!["JOHN DOE", "JANE DOE"];
55+ assert!(new_db
56+ .query("SELECT name FROM users ORDER BY id", &[],)
57+ .unwrap()
58+ .iter()
59+ .map(|row| row.get::<_, String>("name"))
60+ .eq(expected));
61+
62+ // Insert data using old schema and make sure the new schema gets correct values
63+ old_db
64+ .simple_query("INSERT INTO users (id, name) VALUES (3, 'test testsson')")
65+ .unwrap();
66+ let result = new_db
67+ .query_one("SELECT name from users WHERE id = 3", &[])
68+ .unwrap();
69+ assert_eq!("TEST TESTSSON", result.get::<_, &str>("name"));
70+
71+ // Insert data using new schema and make sure the old schema gets correct values
72+ new_db
73+ .simple_query("INSERT INTO users (id, name) VALUES (4, 'TEST TESTSSON')")
74+ .unwrap();
75+ let result = old_db
76+ .query_one("SELECT name from users WHERE id = 4", &[])
77+ .unwrap();
78+ assert_eq!("test testsson", result.get::<_, &str>("name"));
79+ });
10680
107− reshape.complete_migration().unwrap();
81+ test.run();
10882}
10983
11084#[test]
11185fn alter_column_set_not_null() {
112− let (mut reshape, mut old_db, mut new_db) = common::setup();
113−
114− let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
115− name: "users".to_string(),
116− primary_key: vec!["id".to_string()],
117− foreign_keys: vec![],
118− columns: vec![
119− Column {
120− name: "id".to_string(),
121− data_type: "INTEGER".to_string(),
122− nullable: true,
123− default: None,
124− generated: None,
125− },
126− Column {
127− name: "name".to_string(),
128− data_type: "TEXT".to_string(),
129− nullable: true,
130− default: None,
131− generated: None,
132− },
133− ],
134− });
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 {
141− data_type: None,
142− nullable: Some(false),
143− name: None,
144− },
145− });
86+ let mut test = Test::new("Set column not null");
87+
88+ test.first_migration(
89+ r#"
90+ name = "create_user_table"
91+
92+ [[actions]]
93+ type = "create_table"
94+ name = "users"
95+ primary_key = ["id"]
14696
147− let first_migrations = vec![create_users_table.clone()];
148− let second_migrations = vec![create_users_table.clone(), set_name_not_null.clone()];
97+ [[actions.columns]]
98+ name = "id"
99+ type = "INTEGER"
149100
150− // Run first migration, should automatically finish
151− reshape.migrate(first_migrations.clone()).unwrap();
152− assert!(matches!(reshape.state.status, Status::Idle));
153− assert_eq!(
154− Some(&create_users_table.name),
155− reshape.state.current_migration.as_ref()
101+ [[actions.columns]]
102+ name = "name"
103+ type = "TEXT"
104+ "#,
156105 );
157106
158− // Update search paths
159− old_db
160− .simple_query(&reshape::schema_query_for_migration(
161− &first_migrations.last().unwrap().name,
162− ))
163− .unwrap();
107+ test.second_migration(
108+ r#"
109+ name = "set_name_not_null"
110+
111+ [[actions]]
112+ type = "alter_column"
113+ table = "users"
114+ column = "name"
115+ up = "COALESCE(name, 'TEST_DEFAULT_VALUE')"
116+
117+ [actions.changes]
118+ nullable = false
119+ "#,
120+ );
164121
165− // Insert some test users
166− old_db
167− .simple_query(
122+ test.after_first(|db| {
123+ // Insert some test users
124+ db.simple_query(
168125 "
169126 INSERT INTO users (id, name) VALUES
170127 (1, 'John Doe'),
@@ -172,104 +129,180 @@ fn alter_column_set_not_null() {
172129 ",
173130 )
174131 .unwrap();
132+ });
175133
176− // Run second migration
177− reshape.migrate(second_migrations.clone()).unwrap();
178− new_db
179− .simple_query(&reshape::schema_query_for_migration(
180− &second_migrations.last().unwrap().name,
181− ))
182− .unwrap();
134+ test.intermediate(|old_db, new_db| {
135+ // Check that existing users got the correct values
136+ let expected = vec!["John Doe", "TEST_DEFAULT_VALUE"];
137+ assert!(new_db
138+ .query("SELECT name FROM users ORDER BY id", &[],)
139+ .unwrap()
140+ .iter()
141+ .map(|row| row.get::<_, String>("name"))
142+ .eq(expected));
143+
144+ // Insert data using old schema and make sure the new schema gets correct values
145+ old_db
146+ .simple_query("INSERT INTO users (id, name) VALUES (3, NULL)")
147+ .unwrap();
148+ let result = new_db
149+ .query_one("SELECT name from users WHERE id = 3", &[])
150+ .unwrap();
151+ assert_eq!("TEST_DEFAULT_VALUE", result.get::<_, &str>("name"));
152+
153+ // Insert data using new schema and make sure the old schema gets correct values
154+ new_db
155+ .simple_query("INSERT INTO users (id, name) VALUES (4, 'Jane Doe')")
156+ .unwrap();
157+ let result = old_db
158+ .query_one("SELECT name from users WHERE id = 4", &[])
159+ .unwrap();
160+ assert_eq!("Jane Doe", result.get::<_, &str>("name"));
161+
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");
165+ });
183166
184− // Check that existing users got the correct values
185− let expected = vec!["John Doe", "TEST_DEFAULT_VALUE"];
186− assert!(new_db
187− .query("SELECT name FROM users ORDER BY id", &[],)
188− .unwrap()
189− .iter()
190− .map(|row| row.get::<_, String>("name"))
191− .eq(expected));
192−
193− // Insert data using old schema and make sure the new schema gets correct values
194− old_db
195− .simple_query("INSERT INTO users (id, name) VALUES (3, NULL)")
196− .unwrap();
197− let result = new_db
198− .query_one("SELECT name from users WHERE id = 3", &[])
199− .unwrap();
200− assert_eq!("TEST_DEFAULT_VALUE", result.get::<_, &str>("name"));
167+ test.after_completion(|db| {
168+ // Ensure NULL can't be inserted
169+ let result = db.simple_query("INSERT INTO users (id, name) VALUES (5, NULL)");
170+ assert!(result.is_err(), "expected insert to fail");
171+ });
201172
202− // Insert data using new schema and make sure the old schema gets correct values
203− new_db
204− .simple_query("INSERT INTO users (id, name) VALUES (4, 'Jane Doe')")
205− .unwrap();
206− let result = old_db
207− .query_one("SELECT name from users WHERE id = 4", &[])
173+ test.after_abort(|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");
177+ });
178+
179+ test.run();
180+}
181+
182+#[test]
183+fn alter_column_set_nullable() {
184+ let mut test = Test::new("Set column nullable");
185+
186+ test.first_migration(
187+ r#"
188+ name = "create_user_table"
189+
190+ [[actions]]
191+ type = "create_table"
192+ name = "users"
193+ primary_key = ["id"]
194+
195+ [[actions.columns]]
196+ name = "id"
197+ type = "INTEGER"
198+
199+ [[actions.columns]]
200+ name = "name"
201+ type = "TEXT"
202+ nullable = false
203+ "#,
204+ );
205+
206+ test.second_migration(
207+ r#"
208+ name = "set_name_nullable"
209+
210+ [[actions]]
211+ type = "alter_column"
212+ table = "users"
213+ column = "name"
214+ down = "COALESCE(name, 'TEST_DEFAULT_VALUE')"
215+
216+ [actions.changes]
217+ nullable = true
218+ "#,
219+ );
220+
221+ test.after_first(|db| {
222+ // Insert a test user
223+ db.simple_query(
224+ "
225+ INSERT INTO users (id, name) VALUES
226+ (1, 'John Doe')
227+ ",
228+ )
208229 .unwrap();
209− assert_eq!("Jane Doe", result.get::<_, &str>("name"));
230+ });
231+
232+ test.intermediate(|old_db, new_db| {
233+ // Insert data using new schema and make sure the old schema gets correct values
234+ new_db
235+ .simple_query("INSERT INTO users (id, name) VALUES (2, NULL)")
236+ .unwrap();
237+ let result = old_db
238+ .query_one("SELECT name from users WHERE id = 2", &[])
239+ .unwrap();
240+ assert_eq!("TEST_DEFAULT_VALUE", result.get::<_, &str>("name"));
241+
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");
245+
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");
249+ });
250+
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");
255+ });
256+
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");
261+ });
210262
211− reshape.complete_migration().unwrap();
263+ test.run();
212264}
213265
214266#[test]
215267fn alter_column_rename() {
216− let (mut reshape, mut old_db, mut new_db) = common::setup();
217−
218− let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
219− name: "users".to_string(),
220− primary_key: vec!["id".to_string()],
221− foreign_keys: vec![],
222− columns: vec![
223− Column {
224− name: "id".to_string(),
225− data_type: "INTEGER".to_string(),
226− nullable: true,
227− default: None,
228− generated: None,
229− },
230− Column {
231− name: "name".to_string(),
232− data_type: "TEXT".to_string(),
233− nullable: true,
234− default: None,
235− generated: None,
236− },
237− ],
238− });
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
244− down: None,
245− changes: ColumnChanges {
246− data_type: None,
247− nullable: None,
248− name: Some("full_name".to_string()),
249− },
250− });
251−
252− let first_migrations = vec![create_users_table.clone()];
253− let second_migrations = vec![create_users_table.clone(), rename_to_full_name.clone()];
254−
255− // Run first migration, should automatically finish
256− reshape.migrate(first_migrations.clone()).unwrap();
257− assert!(matches!(reshape.state.status, Status::Idle));
258− assert_eq!(
259− Some(&create_users_table.name),
260− reshape.state.current_migration.as_ref()
268+ let mut test = Test::new("Rename column");
269+
270+ test.first_migration(
271+ r#"
272+ name = "create_user_table"
273+
274+ [[actions]]
275+ type = "create_table"
276+ name = "users"
277+ primary_key = ["id"]
278+
279+ [[actions.columns]]
280+ name = "id"
281+ type = "INTEGER"
282+
283+ [[actions.columns]]
284+ name = "name"
285+ type = "TEXT"
286+ "#,
261287 );
262288
263− // Update search paths
264− old_db
265− .simple_query(&reshape::schema_query_for_migration(
266− &first_migrations.last().unwrap().name,
267− ))
268− .unwrap();
289+ test.second_migration(
290+ r#"
291+ name = "set_name_not_null"
292+
293+ [[actions]]
294+ type = "alter_column"
295+ table = "users"
296+ column = "name"
269297
270− // Insert some test data
271− old_db
272− .simple_query(
298+ [actions.changes]
299+ name = "full_name"
300+ "#,
301+ );
302+
303+ test.after_first(|db| {
304+ // Insert some test data
305+ db.simple_query(
273306 "
274307 INSERT INTO users (id, name) VALUES
275308 (1, 'John Doe'),
@@ -277,97 +310,69 @@ fn alter_column_rename() {
277310 ",
278311 )
279312 .unwrap();
313+ });
280314
281− // Run second migration
282− reshape.migrate(second_migrations.clone()).unwrap();
283− new_db
284− .simple_query(&reshape::schema_query_for_migration(
285− &second_migrations.last().unwrap().name,
286− ))
287− .unwrap();
288−
289− // Check that existing values can be queried using new column name
290− let expected = vec!["John Doe", "Jane Doe"];
291− assert!(new_db
292− .query("SELECT full_name FROM users ORDER BY id", &[],)
293− .unwrap()
294− .iter()
295− .map(|row| row.get::<_, String>("full_name"))
296− .eq(expected));
315+ test.intermediate(|_, new_db| {
316+ // Check that existing values can be queried using new column name
317+ let expected = vec!["John Doe", "Jane Doe"];
318+ assert!(new_db
319+ .query("SELECT full_name FROM users ORDER BY id", &[],)
320+ .unwrap()
321+ .iter()
322+ .map(|row| row.get::<_, String>("full_name"))
323+ .eq(expected));
324+ });
297325
298− reshape.complete_migration().unwrap();
326+ test.run();
299327}
300328
301329#[test]
302330fn alter_column_multiple() {
303− let (mut reshape, mut old_db, mut new_db) = common::setup();
304−
305− let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
306− name: "users".to_string(),
307− primary_key: vec!["id".to_string()],
308− foreign_keys: vec![],
309− columns: vec![
310− Column {
311− name: "id".to_string(),
312− data_type: "INTEGER".to_string(),
313− nullable: true,
314− default: None,
315− generated: None,
316− },
317− Column {
318− name: "counter".to_string(),
319− data_type: "INTEGER".to_string(),
320− nullable: false,
321− default: None,
322− generated: None,
323− },
324− ],
325− });
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 {
333− data_type: None,
334− nullable: None,
335− name: None,
336− },
337− })
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 {
344− data_type: None,
345− nullable: None,
346− name: None,
347− },
348− });
349−
350− let first_migrations = vec![create_users_table.clone()];
351− let second_migrations = vec![create_users_table.clone(), increment_counter_twice.clone()];
352−
353− // Run first migration, should automatically finish
354− reshape.migrate(first_migrations.clone()).unwrap();
355− assert!(matches!(reshape.state.status, Status::Idle));
356− assert_eq!(
357− Some(&create_users_table.name),
358− reshape.state.current_migration.as_ref()
331+ let mut test = Test::new("Alter column value multiple times");
332+
333+ test.first_migration(
334+ r#"
335+ name = "create_user_table"
336+
337+ [[actions]]
338+ type = "create_table"
339+ name = "users"
340+ primary_key = ["id"]
341+
342+ [[actions.columns]]
343+ name = "id"
344+ type = "INTEGER"
345+
346+ [[actions.columns]]
347+ name = "counter"
348+ type = "INTEGER"
349+ nullable = false
350+ "#,
359351 );
360352
361− // Update search paths
362− old_db
363− .simple_query(&reshape::schema_query_for_migration(
364− &first_migrations.last().unwrap().name,
365− ))
366− .unwrap();
353+ test.second_migration(
354+ r#"
355+ name = "increment_counter_twice"
356+
357+ [[actions]]
358+ type = "alter_column"
359+ table = "users"
360+ column = "counter"
361+ up = "counter + 1"
362+ down = "counter - 1"
363+
364+ [[actions]]
365+ type = "alter_column"
366+ table = "users"
367+ column = "counter"
368+ up = "counter + 1"
369+ down = "counter - 1"
370+ "#,
371+ );
367372
368− // Insert some test data
369− old_db
370− .simple_query(
373+ test.after_first(|db| {
374+ // Insert some test data
375+ db.simple_query(
371376 "
372377 INSERT INTO users (id, counter) VALUES
373378 (1, 0),
@@ -375,48 +380,355 @@ fn alter_column_multiple() {
375380 ",
376381 )
377382 .unwrap();
383+ });
378384
379− // Run second migration
380− reshape.migrate(second_migrations.clone()).unwrap();
381− new_db
382− .simple_query(&reshape::schema_query_for_migration(
383− &second_migrations.last().unwrap().name,
384− ))
385− .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", &[])
390+ .unwrap()
391+ .iter()
392+ .map(|row| row.get::<_, i32>("counter"))
393+ .collect();
394+ assert_eq!(expected, results);
395+
396+ // Update data using old schema and make sure it was updated for the new schema
397+ old_db
398+ .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
399+ .unwrap();
400+ let result: i32 = new_db
401+ .query("SELECT counter FROM users WHERE id = 1", &[])
402+ .unwrap()
403+ .iter()
404+ .map(|row| row.get("counter"))
405+ .nth(0)
406+ .unwrap();
407+ assert_eq!(52, result);
408+
409+ // Update data using new schema and make sure it was updated for the old schema
410+ new_db
411+ .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
412+ .unwrap();
413+ let result: i32 = old_db
414+ .query("SELECT counter FROM users WHERE id = 1", &[])
415+ .unwrap()
416+ .iter()
417+ .map(|row| row.get("counter"))
418+ .nth(0)
419+ .unwrap();
420+ assert_eq!(48, result);
421+ });
386422
387− // Check that the existing data has been updated
388− let expected = vec![2, 102];
389− let results: Vec<i32> = new_db
390− .query("SELECT counter FROM users ORDER BY id", &[])
391− .unwrap()
392− .iter()
393− .map(|row| row.get::<_, i32>("counter"))
394− .collect();
395− assert_eq!(expected, results);
396−
397− // Update data using old schema and make sure it was updated for the new schema
398− old_db
399− .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
400− .unwrap();
401− let result: i32 = new_db
402− .query("SELECT counter FROM users WHERE id = 1", &[])
403− .unwrap()
404− .iter()
405− .map(|row| row.get("counter"))
406− .nth(0)
407− .unwrap();
408− assert_eq!(52, result);
423+ test.run();
424+}
409425
410− // Update data using new schema and make sure it was updated for the old schema
411− new_db
412− .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
413− .unwrap();
414− let result: i32 = old_db
415− .query("SELECT counter FROM users WHERE id = 1", &[])
416− .unwrap()
417− .iter()
418− .map(|row| row.get("counter"))
419− .nth(0)
426+#[test]
427+fn alter_column_default() {
428+ let mut test = Test::new("Change default value for column");
429+
430+ test.first_migration(
431+ r#"
432+ name = "create_user_table"
433+
434+ [[actions]]
435+ type = "create_table"
436+ name = "users"
437+ primary_key = ["id"]
438+
439+ [[actions.columns]]
440+ name = "id"
441+ type = "INTEGER"
442+
443+ [[actions.columns]]
444+ name = "name"
445+ type = "TEXT"
446+ nullable = false
447+ default = "'DEFAULT'"
448+ "#,
449+ );
450+
451+ test.second_migration(
452+ r#"
453+ name = "change_name_default"
454+
455+ [[actions]]
456+ type = "alter_column"
457+ table = "users"
458+ column = "name"
459+
460+ [actions.changes]
461+ default = "'NEW DEFAULT'"
462+ "#,
463+ );
464+
465+ test.after_first(|db| {
466+ // Insert a test user
467+ db.simple_query(
468+ "
469+ INSERT INTO users (id) VALUES (1)
470+ ",
471+ )
420472 .unwrap();
421− assert_eq!(48, result);
473+ });
474+
475+ test.intermediate(|old_db, new_db| {
476+ // Check that the existing users has the old default value
477+ let expected = vec!["DEFAULT"];
478+ assert!(new_db
479+ .query("SELECT name FROM users", &[],)
480+ .unwrap()
481+ .iter()
482+ .map(|row| row.get::<_, String>("name"))
483+ .eq(expected));
484+
485+ // Insert data using old schema and make those get the old default value
486+ old_db
487+ .simple_query("INSERT INTO users (id) VALUES (2)")
488+ .unwrap();
489+ let result = new_db
490+ .query_one("SELECT name from users WHERE id = 2", &[])
491+ .unwrap();
492+ assert_eq!("DEFAULT", result.get::<_, &str>("name"));
493+
494+ // Insert data using new schema and make sure it gets the new default value
495+ new_db
496+ .simple_query("INSERT INTO users (id) VALUES (3)")
497+ .unwrap();
498+ let result = old_db
499+ .query_one("SELECT name from users WHERE id = 3", &[])
500+ .unwrap();
501+ assert_eq!("NEW DEFAULT", result.get::<_, &str>("name"));
502+ });
503+
504+ test.run();
505+}
506+
507+#[test]
508+fn alter_column_with_index() {
509+ let mut test = Test::new("Alter column with index");
510+
511+ test.first_migration(
512+ r#"
513+ name = "create_user_table"
514+
515+ [[actions]]
516+ type = "create_table"
517+ name = "users"
518+ primary_key = ["id"]
519+
520+ [[actions.columns]]
521+ name = "id"
522+ type = "INTEGER"
523+
524+ [[actions.columns]]
525+ name = "first_name"
526+ type = "TEXT"
527+
528+ [[actions.columns]]
529+ name = "last_name"
530+ type = "TEXT"
531+
532+ [[actions]]
533+ type = "add_index"
534+ table = "users"
535+
536+ [actions.index]
537+ name = "users_name_idx"
538+ columns = ["first_name", "last_name"]
539+ "#,
540+ );
541+
542+ test.second_migration(
543+ r#"
544+ name = "uppercase_last_name"
545+
546+ [[actions]]
547+ type = "alter_column"
548+ table = "users"
549+ column = "last_name"
550+ up = "UPPER(last_name)"
551+ down = "LOWER(last_name)"
552+ "#,
553+ );
554+
555+ test.after_completion(|db| {
556+ // Make sure index still exists
557+ let result: i64 = db
558+ .query(
559+ "
560+ SELECT COUNT(*)
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'
564+ ",
565+ &[],
566+ )
567+ .unwrap()
568+ .first()
569+ .map(|row| row.get(0))
570+ .unwrap();
571+ assert_eq!(1, result, "expected index to still exist");
572+ });
573+
574+ test.run();
575+}
576+
577+#[test]
578+fn alter_column_with_unique_index() {
579+ let mut test = Test::new("Alter column with unique index");
580+
581+ test.first_migration(
582+ r#"
583+ name = "create_user_table"
584+
585+ [[actions]]
586+ type = "create_table"
587+ name = "users"
588+ primary_key = ["id"]
589+
590+ [[actions.columns]]
591+ name = "id"
592+ type = "INTEGER"
593+
594+ [[actions.columns]]
595+ name = "name"
596+ type = "TEXT"
597+
598+ [[actions]]
599+ type = "add_index"
600+ table = "users"
601+
602+ [actions.index]
603+ name = "name_idx"
604+ columns = ["name"]
605+ unique = true
606+ "#,
607+ );
608+
609+ test.second_migration(
610+ r#"
611+ name = "uppercase_name"
612+
613+ [[actions]]
614+ type = "alter_column"
615+ table = "users"
616+ column = "name"
617+ up = "UPPER(name)"
618+ down = "LOWER(name)"
619+ "#,
620+ );
621+
622+ test.after_first(|db| {
623+ db.simple_query("INSERT INTO users (id, name) VALUES (1, 'Test')")
624+ .unwrap();
625+ });
626+
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')");
630+ assert!(
631+ result.is_err(),
632+ "expected duplicate insert to new schema to fail"
633+ );
634+
635+ // Try inserting a value which duplicates the lowercase value of an existing row
636+ new_db
637+ .simple_query("INSERT INTO users (id, name) VALUES (2, 'JOHN')")
638+ .unwrap();
639+ let result = old_db.simple_query("INSERT INTO users (id, name) VALUES (3, 'john')");
640+ assert!(
641+ result.is_err(),
642+ "expected duplicate insert to old schema to fail"
643+ );
644+ });
645+
646+ test.after_completion(|db| {
647+ // Make sure index still exists
648+ let is_unique: bool = db
649+ .query(
650+ "
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'
655+ ",
656+ &[],
657+ )
658+ .unwrap()
659+ .first()
660+ .map(|row| row.get("indisunique"))
661+ .unwrap();
662+ assert!(is_unique, "expected index to still be unique");
663+ });
664+
665+ test.run();
666+}
667+
668+#[test]
669+fn alter_column_with_hash_index() {
670+ let mut test = Test::new("Alter column with custom index type");
671+
672+ test.first_migration(
673+ r#"
674+ name = "create_user_table"
675+
676+ [[actions]]
677+ type = "create_table"
678+ name = "users"
679+ primary_key = ["id"]
680+
681+ [[actions.columns]]
682+ name = "id"
683+ type = "INTEGER"
684+
685+ [[actions.columns]]
686+ name = "name"
687+ type = "TEXT"
688+
689+ [[actions]]
690+ type = "add_index"
691+ table = "users"
692+
693+ [actions.index]
694+ name = "name_idx"
695+ columns = ["name"]
696+ type = "hash"
697+ "#,
698+ );
699+
700+ test.second_migration(
701+ r#"
702+ name = "uppercase_name"
703+
704+ [[actions]]
705+ type = "alter_column"
706+ table = "users"
707+ column = "name"
708+ up = "UPPER(name)"
709+ down = "LOWER(name)"
710+ "#,
711+ );
712+
713+ test.after_completion(|db| {
714+ // Make sure index still has type GIN
715+ let index_type: String = db
716+ .query(
717+ "
718+ SELECT pg_am.amname
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'
723+ ",
724+ &[],
725+ )
726+ .unwrap()
727+ .first()
728+ .map(|row| row.get("amname"))
729+ .unwrap();
730+ assert_eq!("hash", index_type);
731+ });
732+
733+ test.run();
422734}