@@ -1,45 +1,66 @@
1+use reshape::migrations::{AlterColumn, Column, ColumnChanges, CreateTable, Migration};
5fn alter_column_data() {
7fn alter_column_data() {
6− let mut test = Test::new("Alter column");
8+ let (mut reshape, mut old_db, mut new_db) = common::setup();
8− test.first_migration(
10+ let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
9− r#"
11+ name: "users".to_string(),
10− name = "create_user_table"
12+ primary_key: vec!["id".to_string()],
11−
13+ foreign_keys: vec![],
12− [[actions]]
14+ columns: vec![
13− type = "create_table"
15+ Column {
14− name = "users"
16+ name: "id".to_string(),
15− primary_key = ["id"]
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 {
17− [[actions.columns]]
43+ let first_migrations = vec![create_users_table.clone()];
18− name = "id"
44+ let second_migrations = vec![create_users_table.clone(), uppercase_name.clone()];
21− [[actions.columns]]
46+ // Run first migration, should automatically finish
22− name = "name"
47+ reshape.migrate(first_migrations.clone()).unwrap();
23− type = "TEXT"
48+ assert!(matches!(reshape.state.status, Status::Idle));
50+ Some(&create_users_table.name),
51+ reshape.state.current_migration.as_ref()
27− test.second_migration(
54+ // Update search paths
29− name = "uppercase_name"
56+ .simple_query(&reshape::schema_query_for_migration(
30−
57+ &first_migrations.last().unwrap().name,
32− type = "alter_column"
59+ .unwrap();
40− test.after_first(|db| {
61+ // Insert some test users
41− // Insert some test users
62+ old_db
42− db.simple_query(
63+ .simple_query(
44 INSERT INTO users (id, name) VALUES
65 INSERT INTO users (id, name) VALUES
45 (1, 'john Doe'),
66 (1, 'john Doe'),
@@ -47,81 +68,103 @@ fn alter_column_data() {
49 .unwrap();
70 .unwrap();
52− test.intermediate(|old_db, new_db| {
72+ // Run second migration
53− // Check that the existing users has the altered data
73+ reshape.migrate(second_migrations.clone()).unwrap();
54− let expected = vec!["JOHN DOE", "JANE DOE"];
74+ new_db
55− assert!(new_db
75+ .simple_query(&reshape::schema_query_for_migration(
56− .query("SELECT name FROM users ORDER BY id", &[],)
76+ &second_migrations.last().unwrap().name,
58− .iter()
78+ .unwrap();
59− .map(|row| row.get::<_, String>("name"))
79+
60− .eq(expected));
80+ // Check that the existing users has the altered data
61−
81+ let expected = vec!["JOHN DOE", "JANE DOE"];
62− // Insert data using old schema and make sure the new schema gets correct values
82+ assert!(new_db
63− old_db
83+ .query("SELECT name FROM users ORDER BY id", &[],)
64− .simple_query("INSERT INTO users (id, name) VALUES (3, 'test testsson')")
84+ .unwrap()
65− .unwrap();
85+ .iter()
66− let result = new_db
86+ .map(|row| row.get::<_, String>("name"))
67− .query_one("SELECT name from users WHERE id = 3", &[])
87+ .eq(expected));
69− assert_eq!("TEST TESTSSON", result.get::<_, &str>("name"));
89+ // Insert data using old schema and make sure the new schema gets correct values
71− // Insert data using new schema and make sure the old schema gets correct values
91+ .simple_query("INSERT INTO users (id, name) VALUES (3, 'test testsson')")
73− .simple_query("INSERT INTO users (id, name) VALUES (4, 'TEST TESTSSON')")
93+ let result = new_db
74− .unwrap();
94+ .query_one("SELECT name from users WHERE id = 3", &[])
75− let result = old_db
95+ .unwrap();
76− .query_one("SELECT name from users WHERE id = 4", &[])
96+ assert_eq!("TEST TESTSSON", result.get::<_, &str>("name"));
78− assert_eq!("test testsson", result.get::<_, &str>("name"));
98+ // 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')")
103+ .query_one("SELECT name from users WHERE id = 4", &[])
105+ assert_eq!("test testsson", result.get::<_, &str>("name"));
81− test.run();
107+ reshape.complete_migration().unwrap();
85fn alter_column_set_not_null() {
111fn alter_column_set_not_null() {
86− let mut test = Test::new("Set column not null");
112+ let (mut reshape, mut old_db, mut new_db) = common::setup();
88− test.first_migration(
114+ let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
89− r#"
115+ name: "users".to_string(),
90− name = "create_user_table"
116+ primary_key: vec!["id".to_string()],
91−
117+ foreign_keys: vec![],
92− [[actions]]
118+ columns: vec![
93− type = "create_table"
119+ Column {
94− name = "users"
120+ name: "id".to_string(),
95− primary_key = ["id"]
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),
97− [[actions.columns]]
147+ let first_migrations = vec![create_users_table.clone()];
98− name = "id"
148+ let second_migrations = vec![create_users_table.clone(), set_name_not_null.clone()];
101− [[actions.columns]]
150+ // Run first migration, should automatically finish
102− name = "name"
151+ reshape.migrate(first_migrations.clone()).unwrap();
103− type = "TEXT"
152+ assert!(matches!(reshape.state.status, Status::Idle));
154+ Some(&create_users_table.name),
155+ reshape.state.current_migration.as_ref()
107− test.second_migration(
158+ // Update search paths
109− name = "set_name_not_null"
160+ .simple_query(&reshape::schema_query_for_migration(
110−
161+ &first_migrations.last().unwrap().name,
112− type = "alter_column"
163+ .unwrap();
115− up = "COALESCE(name, 'TEST_DEFAULT_VALUE')"
122− test.after_first(|db| {
165+ // Insert some test users
123− // Insert some test users
166+ old_db
124− db.simple_query(
167+ .simple_query(
126 INSERT INTO users (id, name) VALUES
169 INSERT INTO users (id, name) VALUES
127 (1, 'John Doe'),
170 (1, 'John Doe'),
@@ -129,180 +172,104 @@ fn alter_column_set_not_null() {
131 .unwrap();
174 .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"];
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)")
149− .query_one("SELECT name from users WHERE id = 3", &[])
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");
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");
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");
179− test.run();
176+ // Run second migration
180−}
177+ reshape.migrate(second_migrations.clone()).unwrap();
182−#[test]
179+ .simple_query(&reshape::schema_query_for_migration(
183−fn alter_column_set_nullable() {
180+ &second_migrations.last().unwrap().name,
184− let mut test = Test::new("Set column nullable");
181+ ))
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
225− INSERT INTO users (id, name) VALUES
229 .unwrap();
182 .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", &[])
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| {
184+ // Check that existing users got the correct values
252− // Ensure NULL can be inserted
185+ let expected = vec!["John Doe", "TEST_DEFAULT_VALUE"];
253− let result = db.simple_query("INSERT INTO users (id, name) VALUES (5, NULL)");
186+ assert!(new_db
254− assert!(result.is_ok(), "expected insert to succeed");
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)")
198+ .query_one("SELECT name from users WHERE id = 3", &[])
200+ assert_eq!("TEST_DEFAULT_VALUE", result.get::<_, &str>("name"));
257− test.after_abort(|db| {
202+ // Insert data using new schema and make sure the old schema gets correct values
258− // Ensure NULL can't be inserted
203+ new_db
259− let result = db.simple_query("INSERT INTO users (id, name) VALUES (5, NULL)");
204+ .simple_query("INSERT INTO users (id, name) VALUES (4, 'Jane Doe')")
260− assert!(result.is_err(), "expected insert to fail");
205+ .unwrap();
261− });
206+ let result = old_db
207+ .query_one("SELECT name from users WHERE id = 4", &[])
209+ assert_eq!("Jane Doe", result.get::<_, &str>("name"));
263− test.run();
211+ reshape.complete_migration().unwrap();
267fn alter_column_rename() {
215fn alter_column_rename() {
268− let mut test = Test::new("Rename column");
216+ let (mut reshape, mut old_db, mut new_db) = common::setup();
270− test.first_migration(
218+ let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
271− r#"
219+ name: "users".to_string(),
272− name = "create_user_table"
220+ primary_key: vec!["id".to_string()],
273−
221+ foreign_keys: vec![],
274− [[actions]]
222+ columns: vec![
275− type = "create_table"
223+ Column {
276− name = "users"
224+ name: "id".to_string(),
277− primary_key = ["id"]
225+ data_type: "INTEGER".to_string(),
279− [[actions.columns]]
227+ default: None,
280− name = "id"
228+ generated: None,
281− type = "INTEGER"
229+ },
283− [[actions.columns]]
231+ name: "name".to_string(),
284− name = "name"
232+ data_type: "TEXT".to_string(),
285− type = "TEXT"
233+ nullable: true,
286− "#,
234+ default: None,
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()
289− test.second_migration(
263+ // Update search paths
291− name = "set_name_not_null"
265+ .simple_query(&reshape::schema_query_for_migration(
292−
266+ &first_migrations.last().unwrap().name,
294− type = "alter_column"
268+ .unwrap();
303− test.after_first(|db| {
270+ // Insert some test data
304− // Insert some test data
271+ old_db
305− db.simple_query(
272+ .simple_query(
307 INSERT INTO users (id, name) VALUES
274 INSERT INTO users (id, name) VALUES
308 (1, 'John Doe'),
275 (1, 'John Doe'),
@@ -310,69 +277,97 @@ fn alter_column_rename() {
312 .unwrap();
279 .unwrap();
315− test.intermediate(|_, new_db| {
281+ // Run second migration
316− // Check that existing values can be queried using new column name
282+ reshape.migrate(second_migrations.clone()).unwrap();
317− let expected = vec!["John Doe", "Jane Doe"];
283+ new_db
318− assert!(new_db
284+ .simple_query(&reshape::schema_query_for_migration(
319− .query("SELECT full_name FROM users ORDER BY id", &[],)
285+ &second_migrations.last().unwrap().name,
321− .iter()
287+ .unwrap();
322− .map(|row| row.get::<_, String>("full_name"))
326− test.run();
289+ // Check that existing values can be queried using new column name
290+ let expected = vec!["John Doe", "Jane Doe"];
292+ .query("SELECT full_name FROM users ORDER BY id", &[],)
295+ .map(|row| row.get::<_, String>("full_name"))
298+ reshape.complete_migration().unwrap();
330fn alter_column_multiple() {
302fn alter_column_multiple() {
331− let mut test = Test::new("Alter column value multiple times");
303+ let (mut reshape, mut old_db, mut new_db) = common::setup();
333− test.first_migration(
305+ let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
334− r#"
306+ name: "users".to_string(),
335− name = "create_user_table"
307+ primary_key: vec!["id".to_string()],
336−
308+ foreign_keys: vec![],
337− [[actions]]
309+ columns: vec![
338− type = "create_table"
310+ Column {
339− name = "users"
311+ name: "id".to_string(),
340− primary_key = ["id"]
312+ data_type: "INTEGER".to_string(),
342− [[actions.columns]]
314+ default: None,
343− name = "id"
315+ generated: None,
344− type = "INTEGER"
316+ },
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− "#,
322+ generated: None,
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()
353− test.second_migration(
361+ // Update search paths
355− name = "increment_counter_twice"
363+ .simple_query(&reshape::schema_query_for_migration(
356−
364+ &first_migrations.last().unwrap().name,
358− type = "alter_column"
366+ .unwrap();
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
369+ old_db
375− db.simple_query(
370+ .simple_query(
377 INSERT INTO users (id, counter) VALUES
372 INSERT INTO users (id, counter) VALUES
@@ -380,355 +375,48 @@ fn alter_column_multiple() {
382 .unwrap();
377 .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);
427−fn alter_column_default() {
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| {
379+ // Run second migration
466− // Insert a test user
380+ reshape.migrate(second_migrations.clone()).unwrap();
467− db.simple_query(
381+ new_db
468− "
382+ .simple_query(&reshape::schema_query_for_migration(
469− INSERT INTO users (id) VALUES (1)
383+ &second_migrations.last().unwrap().name,
472 .unwrap();
385 .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(
387+ // Check that the existing data has been updated
701− r#"
388+ let expected = vec![2, 102];
702− name = "uppercase_name"
389+ let results: Vec<i32> = new_db
703−
390+ .query("SELECT counter FROM users ORDER BY id", &[])
704− [[actions]]
391+ .unwrap()
705− type = "alter_column"
392+ .iter()
706− table = "users"
393+ .map(|row| row.get::<_, i32>("counter"))
707− column = "name"
394+ .collect();
708− up = "UPPER(name)"
395+ assert_eq!(expected, results);
709− down = "LOWER(name)"
396+
710− "#,
397+ // Update data using old schema and make sure it was updated for the new schema
712−
399+ .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
713− test.after_completion(|db| {
400+ .unwrap();
714− // Make sure index still has type GIN
401+ let result: i32 = new_db
715− let index_type: String = db
402+ .query("SELECT counter FROM users WHERE id = 1", &[])
716− .query(
403+ .unwrap()
718− SELECT pg_am.amname
405+ .map(|row| row.get("counter"))
719− FROM pg_catalog.pg_index
406+ .nth(0)
720− JOIN pg_catalog.pg_class ON pg_index.indexrelid = pg_class.oid
407+ .unwrap();
721− JOIN pg_catalog.pg_am ON pg_class.relam = pg_am.oid
408+ assert_eq!(52, result);
722− WHERE pg_class.relname = 'name_idx'
728− .map(|row| row.get("amname"))
730− assert_eq!("hash", index_type);
733− test.run();
410+ // Update data using new schema and make sure it was updated for the old schema
412+ .query("UPDATE users SET counter = 50 WHERE id = 1", &[])
414+ let result: i32 = old_db
415+ .query("SELECT counter FROM users WHERE id = 1", &[])
418+ .map(|row| row.get("counter"))
421+ assert_eq!(48, result);