@@ -1,61 +1,45 @@
1−use reshape::migrations::{
2− AddIndex, AlterColumn, ColumnBuilder, ColumnChanges, CreateTableBuilder, Index, Migration,
3−};
4−
51mod common;
2+use common::Test;
63
74#[test]
85fn alter_column_data() {
9− let (mut reshape, mut old_db, mut new_db) = common::setup();
10−
11− let create_users_table = Migration::new("create_user_table", None).with_action(
12− CreateTableBuilder::default()
13− .name("users")
14− .primary_key(vec!["id".to_string()])
15− .columns(vec![
16− ColumnBuilder::default()
17− .name("id")
18− .data_type("INTEGER")
19− .build()
20− .unwrap(),
21− ColumnBuilder::default()
22− .name("name")
23− .data_type("TEXT")
24− .build()
25− .unwrap(),
26− ])
27− .build()
28− .unwrap(),
29− );
30− let uppercase_name = Migration::new("uppercase_name", None).with_action(AlterColumn {
31− table: "users".to_string(),
32− column: "name".to_string(),
33− up: Some("UPPER(name)".to_string()),
34− down: Some("LOWER(name)".to_string()),
35− changes: ColumnChanges {
36− data_type: None,
37− nullable: None,
38− name: None,
39− default: None,
40− },
41− });
6+ let mut test = Test::new("Alter column");
427
43− let first_migrations = vec![create_users_table.clone()];
44− let second_migrations = vec![create_users_table.clone(), uppercase_name.clone()];
8+ test.first_migration(
9+ r#"
10+ name = "create_user_table"
4511
46− // Run first migration, should automatically finish
47− reshape.migrate(first_migrations.clone()).unwrap();
12+ [[actions]]
13+ type = "create_table"
14+ name = "users"
15+ primary_key = ["id"]
4816
49− // Update search paths
50− old_db
51− .simple_query(&reshape::schema_query_for_migration(
52− &first_migrations.last().unwrap().name,
53− ))
54− .unwrap();
17+ [[actions.columns]]
18+ name = "id"
19+ type = "INTEGER"
20+
21+ [[actions.columns]]
22+ name = "name"
23+ type = "TEXT"
24+ "#,
25+ );
26+
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+ );
5539
56− // Insert some test users
57− old_db
58− .simple_query(
40+ test.after_first(|db| {
41+ // Insert some test users
42+ db.simple_query(
5943 "
6044 INSERT INTO users (id, name) VALUES
6145 (1, 'john Doe'),
@@ -63,98 +47,81 @@ fn alter_column_data() {
6347 ",
6448 )
6549 .unwrap();
50+ });
6651
67− // Run second migration
68− reshape.migrate(second_migrations.clone()).unwrap();
69− new_db
70− .simple_query(&reshape::schema_query_for_migration(
71− &second_migrations.last().unwrap().name,
72− ))
73− .unwrap();
74−
75− // Check that the existing users has the altered data
76− let expected = vec!["JOHN DOE", "JANE DOE"];
77− assert!(new_db
78− .query("SELECT name FROM users ORDER BY id", &[],)
79− .unwrap()
80− .iter()
81− .map(|row| row.get::<_, String>("name"))
82− .eq(expected));
83−
84− // Insert data using old schema and make sure the new schema gets correct values
85− old_db
86− .simple_query("INSERT INTO users (id, name) VALUES (3, 'test testsson')")
87− .unwrap();
88− let result = new_db
89− .query_one("SELECT name from users WHERE id = 3", &[])
90− .unwrap();
91− assert_eq!("TEST TESTSSON", result.get::<_, &str>("name"));
92−
93− // Insert data using new schema and make sure the old schema gets correct values
94− new_db
95− .simple_query("INSERT INTO users (id, name) VALUES (4, 'TEST TESTSSON')")
96− .unwrap();
97− let result = old_db
98− .query_one("SELECT name from users WHERE id = 4", &[])
99− .unwrap();
100− 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+ });
10180
102− reshape.complete().unwrap();
103− common::assert_cleaned_up(&mut new_db);
81+ test.run();
10482}
10583
10684#[test]
10785fn alter_column_set_not_null() {
108− let (mut reshape, mut old_db, mut new_db) = common::setup();
109−
110− let create_users_table = Migration::new("create_user_table", None).with_action(
111− CreateTableBuilder::default()
112− .name("users")
113− .primary_key(vec!["id".to_string()])
114− .columns(vec![
115− ColumnBuilder::default()
116− .name("id")
117− .data_type("INTEGER")
118− .build()
119− .unwrap(),
120− ColumnBuilder::default()
121− .name("name")
122− .data_type("TEXT")
123− .build()
124− .unwrap(),
125− ])
126− .build()
127− .unwrap(),
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"]
96+
97+ [[actions.columns]]
98+ name = "id"
99+ type = "INTEGER"
100+
101+ [[actions.columns]]
102+ name = "name"
103+ type = "TEXT"
104+ "#,
128105 );
129− let set_name_not_null = Migration::new("set_name_not_null", None).with_action(AlterColumn {
130− table: "users".to_string(),
131− column: "name".to_string(),
132− up: Some("COALESCE(name, 'TEST_DEFAULT_VALUE')".to_string()),
133− down: Some("name".to_string()),
134− changes: ColumnChanges {
135− data_type: None,
136− nullable: Some(false),
137− name: None,
138− default: None,
139− },
140− });
141106
142− let first_migrations = vec![create_users_table.clone()];
143− let second_migrations = vec![create_users_table.clone(), set_name_not_null.clone()];
107+ test.second_migration(
108+ r#"
109+ name = "set_name_not_null"
144110
145− // Run first migration, should automatically finish
146− reshape.migrate(first_migrations.clone()).unwrap();
111+ [[actions]]
112+ type = "alter_column"
113+ table = "users"
114+ column = "name"
115+ up = "COALESCE(name, 'TEST_DEFAULT_VALUE')"
147116
148− // Update search paths
149− old_db
150− .simple_query(&reshape::schema_query_for_migration(
151− &first_migrations.last().unwrap().name,
152− ))
153− .unwrap();
117+ [actions.changes]
118+ nullable = false
119+ "#,
120+ );
154121
155− // Insert some test users
156− old_db
157− .simple_query(
122+ test.after_first(|db| {
123+ // Insert some test users
124+ db.simple_query(
158125 "
159126 INSERT INTO users (id, name) VALUES
160127 (1, 'John Doe'),
@@ -162,99 +129,180 @@ fn alter_column_set_not_null() {
162129 ",
163130 )
164131 .unwrap();
132+ });
165133
166− // Run second migration
167− reshape.migrate(second_migrations.clone()).unwrap();
168− new_db
169− .simple_query(&reshape::schema_query_for_migration(
170− &second_migrations.last().unwrap().name,
171− ))
172− .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+ });
173166
174− // Check that existing users got the correct values
175− let expected = vec!["John Doe", "TEST_DEFAULT_VALUE"];
176− assert!(new_db
177− .query("SELECT name FROM users ORDER BY id", &[],)
178− .unwrap()
179− .iter()
180− .map(|row| row.get::<_, String>("name"))
181− .eq(expected));
182−
183− // Insert data using old schema and make sure the new schema gets correct values
184− old_db
185− .simple_query("INSERT INTO users (id, name) VALUES (3, NULL)")
186− .unwrap();
187− let result = new_db
188− .query_one("SELECT name from users WHERE id = 3", &[])
189− .unwrap();
190− 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+ });
191172
192− // Insert data using new schema and make sure the old schema gets correct values
193− new_db
194− .simple_query("INSERT INTO users (id, name) VALUES (4, 'Jane Doe')")
195− .unwrap();
196− let result = old_db
197− .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+ )
198229 .unwrap();
199− assert_eq!("Jane Doe", result.get::<_, &str>("name"));
230+ });
200231
201− reshape.complete().unwrap();
202− common::assert_cleaned_up(&mut new_db);
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+ });
262+
263+ test.run();
203264}
204265
205266#[test]
206267fn alter_column_rename() {
207− let (mut reshape, mut old_db, mut new_db) = common::setup();
208−
209− let create_users_table = Migration::new("create_user_table", None).with_action(
210− CreateTableBuilder::default()
211− .name("users")
212− .primary_key(vec!["id".to_string()])
213− .columns(vec![
214− ColumnBuilder::default()
215− .name("id")
216− .data_type("INTEGER")
217− .build()
218− .unwrap(),
219− ColumnBuilder::default()
220− .name("name")
221− .data_type("TEXT")
222− .build()
223− .unwrap(),
224− ])
225− .build()
226− .unwrap(),
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+ "#,
227287 );
228− let rename_to_full_name =
229− Migration::new("rename_to_full_name", None).with_action(AlterColumn {
230− table: "users".to_string(),
231− column: "name".to_string(),
232− up: None, // up and down are not required when only renaming a column
233− down: None,
234− changes: ColumnChanges {
235− data_type: None,
236− nullable: None,
237− name: Some("full_name".to_string()),
238− default: None,
239− },
240− });
241−
242− let first_migrations = vec![create_users_table.clone()];
243− let second_migrations = vec![create_users_table.clone(), rename_to_full_name.clone()];
244−
245− // Run first migration, should automatically finish
246− reshape.migrate(first_migrations.clone()).unwrap();
247−
248− // Update search paths
249− old_db
250− .simple_query(&reshape::schema_query_for_migration(
251− &first_migrations.last().unwrap().name,
252− ))
253− .unwrap();
254288
255− // Insert some test data
256− old_db
257− .simple_query(
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"
297+
298+ [actions.changes]
299+ name = "full_name"
300+ "#,
301+ );
302+
303+ test.after_first(|db| {
304+ // Insert some test data
305+ db.simple_query(
258306 "
259307 INSERT INTO users (id, name) VALUES
260308 (1, 'John Doe'),
@@ -262,94 +310,69 @@ fn alter_column_rename() {
262310 ",
263311 )
264312 .unwrap();
313+ });
265314
266− // Run second migration
267− reshape.migrate(second_migrations.clone()).unwrap();
268− new_db
269− .simple_query(&reshape::schema_query_for_migration(
270− &second_migrations.last().unwrap().name,
271− ))
272− .unwrap();
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+ });
273325
274− // Check that existing values can be queried using new column name
275− let expected = vec!["John Doe", "Jane Doe"];
276− assert!(new_db
277− .query("SELECT full_name FROM users ORDER BY id", &[],)
278− .unwrap()
279− .iter()
280− .map(|row| row.get::<_, String>("full_name"))
281− .eq(expected));
282−
283− reshape.complete().unwrap();
284− common::assert_cleaned_up(&mut new_db);
326+ test.run();
285327}
286328
287329#[test]
288330fn alter_column_multiple() {
289− let (mut reshape, mut old_db, mut new_db) = common::setup();
290−
291− let create_users_table = Migration::new("create_user_table", None).with_action(
292− CreateTableBuilder::default()
293− .name("users")
294− .primary_key(vec!["id".to_string()])
295− .columns(vec![
296− ColumnBuilder::default()
297− .name("id")
298− .data_type("INTEGER")
299− .build()
300− .unwrap(),
301− ColumnBuilder::default()
302− .name("counter")
303− .data_type("INTEGER")
304− .nullable(false)
305− .build()
306− .unwrap(),
307− ])
308− .build()
309− .unwrap(),
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+ "#,
351+ );
352+
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+ "#,
310371 );
311− let increment_counter_twice = Migration::new("increment_counter_twice", None)
312− .with_action(AlterColumn {
313− table: "users".to_string(),
314− column: "counter".to_string(),
315− up: Some("counter + 1".to_string()),
316− down: Some("counter - 1".to_string()),
317− changes: ColumnChanges {
318− data_type: None,
319− nullable: None,
320− name: None,
321− default: None,
322− },
323− })
324− .with_action(AlterColumn {
325− table: "users".to_string(),
326− column: "counter".to_string(),
327− up: Some("counter + 1".to_string()),
328− down: Some("counter - 1".to_string()),
329− changes: ColumnChanges {
330− data_type: None,
331− nullable: None,
332− name: None,
333− default: None,
334− },
335− });
336−
337− let first_migrations = vec![create_users_table.clone()];
338− let second_migrations = vec![create_users_table.clone(), increment_counter_twice.clone()];
339−
340− // Run first migration, should automatically finish
341− reshape.migrate(first_migrations.clone()).unwrap();
342−
343− // Update search paths
344− old_db
345− .simple_query(&reshape::schema_query_for_migration(
346− &first_migrations.last().unwrap().name,
347− ))
348− .unwrap();
349372
350− // Insert some test data
351− old_db
352− .simple_query(
373+ test.after_first(|db| {
374+ // Insert some test data
375+ db.simple_query(
353376 "
354377 INSERT INTO users (id, counter) VALUES
355378 (1, 0),
@@ -357,239 +380,355 @@ fn alter_column_multiple() {
357380 ",
358381 )
359382 .unwrap();
383+ });
360384
361− // Run second migration
362− reshape.migrate(second_migrations.clone()).unwrap();
363− new_db
364− .simple_query(&reshape::schema_query_for_migration(
365− &second_migrations.last().unwrap().name,
366− ))
367− .unwrap();
368−
369− // Check that the existing data has been updated
370− let expected = vec![2, 102];
371− let results: Vec<i32> = new_db
372− .query("SELECT counter FROM users ORDER BY id", &[])
373− .unwrap()
374− .iter()
375− .map(|row| row.get::<_, i32>("counter"))
376− .collect();
377− assert_eq!(expected, results);
378−
379− // Update data using old schema and make sure it was updated for the new schema
380− old_db
381− .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
382− .unwrap();
383− let result: i32 = new_db
384− .query("SELECT counter FROM users WHERE id = 1", &[])
385− .unwrap()
386− .iter()
387− .map(|row| row.get("counter"))
388− .nth(0)
389− .unwrap();
390− assert_eq!(52, result);
391−
392− // Update data using new schema and make sure it was updated for the old schema
393− new_db
394− .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
395− .unwrap();
396− let result: i32 = old_db
397− .query("SELECT counter FROM users WHERE id = 1", &[])
398− .unwrap()
399− .iter()
400− .map(|row| row.get("counter"))
401− .nth(0)
402− .unwrap();
403− assert_eq!(48, result);
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+ });
404422
405− reshape.complete().unwrap();
406− common::assert_cleaned_up(&mut new_db);
423+ test.run();
407424}
408425
409426#[test]
410427fn alter_column_default() {
411− let (mut reshape, mut old_db, mut new_db) = common::setup();
412−
413− let create_users_table = Migration::new("create_user_table", None).with_action(
414− CreateTableBuilder::default()
415− .name("users")
416− .primary_key(vec!["id".to_string()])
417− .columns(vec![
418− ColumnBuilder::default()
419− .name("id")
420− .data_type("INTEGER")
421− .build()
422− .unwrap(),
423− ColumnBuilder::default()
424− .name("name")
425− .data_type("TEXT")
426− .nullable(false)
427− .default_value("'DEFAULT'")
428− .build()
429− .unwrap(),
430− ])
431− .build()
432− .unwrap(),
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+ "#,
433449 );
434− let change_name_default =
435− Migration::new("change_name_default", None).with_action(AlterColumn {
436− table: "users".to_string(),
437− column: "name".to_string(),
438− up: None,
439− down: None,
440− changes: ColumnChanges {
441− data_type: None,
442− nullable: None,
443− name: None,
444− default: Some("'NEW DEFAULT'".to_string()),
445− },
446− });
447−
448− let first_migrations = vec![create_users_table.clone()];
449− let second_migrations = vec![create_users_table.clone(), change_name_default.clone()];
450−
451− // Run first migration, should automatically finish
452− reshape.migrate(first_migrations.clone()).unwrap();
453−
454− // Update search paths
455− old_db
456− .simple_query(&reshape::schema_query_for_migration(
457− &first_migrations.last().unwrap().name,
458− ))
459− .unwrap();
460450
461− // Insert a test user
462− old_db
463− .simple_query(
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(
464468 "
465469 INSERT INTO users (id) VALUES (1)
466470 ",
467471 )
468472 .unwrap();
473+ });
469474
470− // Run second migration
471− reshape.migrate(second_migrations.clone()).unwrap();
472− new_db
473− .simple_query(&reshape::schema_query_for_migration(
474− &second_migrations.last().unwrap().name,
475− ))
476− .unwrap();
477−
478− // Check that the existing users has the old default value
479− let expected = vec!["DEFAULT"];
480− assert!(new_db
481− .query("SELECT name FROM users", &[],)
482− .unwrap()
483− .iter()
484− .map(|row| row.get::<_, String>("name"))
485− .eq(expected));
486−
487− // Insert data using old schema and make those get the old default value
488− old_db
489− .simple_query("INSERT INTO users (id) VALUES (2)")
490− .unwrap();
491− let result = new_db
492− .query_one("SELECT name from users WHERE id = 2", &[])
493− .unwrap();
494− assert_eq!("DEFAULT", result.get::<_, &str>("name"));
495−
496− // Insert data using new schema and make sure it gets the new default value
497− new_db
498− .simple_query("INSERT INTO users (id) VALUES (3)")
499− .unwrap();
500− let result = old_db
501− .query_one("SELECT name from users WHERE id = 3", &[])
502− .unwrap();
503− assert_eq!("NEW DEFAULT", result.get::<_, &str>("name"));
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+ });
504503
505− reshape.complete().unwrap();
506− common::assert_cleaned_up(&mut new_db);
504+ test.run();
507505}
508506
509507#[test]
510508fn alter_column_with_index() {
511− let (mut reshape, mut db, _) = common::setup();
512−
513− let create_users_table = Migration::new("create_user_table", None)
514− .with_action(
515− CreateTableBuilder::default()
516− .name("users")
517− .primary_key(vec!["id".to_string()])
518− .columns(vec![
519− ColumnBuilder::default()
520− .name("id")
521− .data_type("INTEGER")
522− .build()
523− .unwrap(),
524− ColumnBuilder::default()
525− .name("first_name")
526− .data_type("TEXT")
527− .build()
528− .unwrap(),
529− ColumnBuilder::default()
530− .name("last_name")
531− .data_type("TEXT")
532− .build()
533− .unwrap(),
534− ])
535− .build()
536− .unwrap(),
537− )
538− .with_action(AddIndex {
539− table: "users".to_string(),
540− index: Index {
541− name: "users_name_idx".to_string(),
542− columns: vec!["first_name".to_string(), "last_name".to_string()],
543− unique: false,
544− index_type: None,
545− },
546− });
547− let uppercase_name = Migration::new("uppercase_name", None).with_action(AlterColumn {
548− table: "users".to_string(),
549− column: "last_name".to_string(),
550− up: Some("UPPER(last_name)".to_string()),
551− down: Some("LOWER(last_name)".to_string()),
552− changes: ColumnChanges {
553− data_type: None,
554− nullable: None,
555− name: None,
556− default: None,
557− },
558− });
509+ let mut test = Test::new("Alter column with index");
559510
560− let first_migrations = vec![create_users_table.clone()];
561− let second_migrations = vec![create_users_table.clone(), uppercase_name.clone()];
511+ test.first_migration(
512+ r#"
513+ name = "create_user_table"
562514
563− // Run first migration, should automatically finish
564− reshape.migrate(first_migrations.clone()).unwrap();
515+ [[actions]]
516+ type = "create_table"
517+ name = "users"
518+ primary_key = ["id"]
565519
566− // Run second migration
567− reshape.migrate(second_migrations.clone()).unwrap();
568− db.simple_query(&reshape::schema_query_for_migration(
569− &second_migrations.last().unwrap().name,
570− ))
571− .unwrap();
520+ [[actions.columns]]
521+ name = "id"
522+ type = "INTEGER"
572523
573− // Complete the second migration which should replace the existing column
574− // with the temporary one
575− reshape.complete().unwrap();
524+ [[actions.columns]]
525+ name = "first_name"
526+ type = "TEXT"
576527
577− // Make sure index still exists
578− let result: i64 = db
579− .query(
580− "
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+ "
581560 SELECT COUNT(*)
582561 FROM pg_catalog.pg_index
583562 JOIN pg_catalog.pg_class ON pg_index.indexrelid = pg_class.oid
584563 WHERE pg_class.relname = 'users_name_idx'
585564 ",
586− &[],
587− )
588− .unwrap()
589− .first()
590− .map(|row| row.get(0))
591− .unwrap();
592− assert_eq!(1, result, "expected index to still exist");
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+ });
593732
594− common::assert_cleaned_up(&mut db);
733+ test.run();
595734}