@@ -0,0 +1,293 @@
1+use reshape::migrations::{AddColumn, Column, CreateTable, Migration};
8+ let (mut reshape, mut old_db, mut new_db) = common::setup();
10+ let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
11+ name: "users".to_string(),
14+ name: "id".to_string(),
15+ data_type: "SERIAL".to_string(),
20+ name: "name".to_string(),
21+ data_type: "TEXT".to_string(),
27+ let add_first_last_name_columns = Migration::new("add_first_and_last_name_columns", None)
28+ .with_action(AddColumn {
29+ table: "users".to_string(),
31+ name: "first".to_string(),
32+ data_type: "TEXT".to_string(),
36+ up: Some("(STRING_TO_ARRAY(name, ' '))[1]".to_string()),
38+ .with_action(AddColumn {
39+ table: "users".to_string(),
41+ name: "last".to_string(),
42+ data_type: "TEXT".to_string(),
46+ up: Some("(STRING_TO_ARRAY(name, ' '))[2]".to_string()),
49+ let first_migrations = vec![create_users_table.clone()];
50+ let second_migrations = vec![
51+ create_users_table.clone(),
52+ add_first_last_name_columns.clone(),
55+ // Run first migration, should automatically finish
56+ reshape.migrate(first_migrations.clone()).unwrap();
57+ assert!(matches!(reshape.state.status, Status::Idle));
59+ Some(&create_users_table.name),
60+ reshape.state.current_migration.as_ref()
63+ // Update search paths
65+ .simple_query(&reshape::generate_schema_query(
66+ &first_migrations.last().unwrap().name,
70+ .simple_query(&reshape::generate_schema_query(
71+ &first_migrations.last().unwrap().name,
75+ // Insert some test users
79+ INSERT INTO users (id, name) VALUES
86+ // Run second migration
87+ reshape.migrate(second_migrations.clone()).unwrap();
89+ .simple_query(&reshape::generate_schema_query(
90+ &second_migrations.last().unwrap().name,
94+ // Check that the existing users have the new columns populated
95+ let expected = vec![("John", "Doe"), ("Jane", "Doe")];
97+ .query("SELECT first, last FROM users ORDER BY id", &[],)
100+ .map(|row| (row.get("first"), row.get("last")))
103+ // Insert data using old schema and make sure the new columns are populated
105+ .simple_query("INSERT INTO users (id, name) VALUES (3, 'Test Testsson')")
107+ let (first_name, last_name): (String, String) = new_db
108+ .query_one("SELECT first, last from users WHERE id = 3", &[])
109+ .map(|row| (row.get("first"), row.get("last")))
112+ ("Test", "Testsson"),
113+ (first_name.as_ref(), last_name.as_ref())
116+ reshape.complete_migration().unwrap();
120+fn add_column_nullable() {
121+ let (mut reshape, mut old_db, mut new_db) = common::setup();
123+ let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
124+ name: "users".to_string(),
125+ columns: vec![Column {
126+ name: "id".to_string(),
127+ data_type: "SERIAL".to_string(),
132+ let add_name_column = Migration::new("add_nullable_name_column", None).with_action(AddColumn {
133+ table: "users".to_string(),
135+ name: "name".to_string(),
136+ data_type: "TEXT".to_string(),
143+ let first_migrations = vec![create_users_table.clone()];
144+ let second_migrations = vec![create_users_table.clone(), add_name_column.clone()];
146+ // Run first migration, should automatically finish
147+ reshape.migrate(first_migrations.clone()).unwrap();
148+ assert!(matches!(reshape.state.status, Status::Idle));
150+ Some(&create_users_table.name),
151+ reshape.state.current_migration.as_ref()
154+ // Update search paths
156+ .simple_query(&reshape::generate_schema_query(
157+ &first_migrations.last().unwrap().name,
161+ .simple_query(&reshape::generate_schema_query(
162+ &first_migrations.last().unwrap().name,
166+ // Insert some test values
170+ INSERT INTO users (id) VALUES
177+ // Run second migration
178+ reshape.migrate(second_migrations.clone()).unwrap();
180+ .simple_query(&reshape::generate_schema_query(
181+ &second_migrations.last().unwrap().name,
185+ // Ensure existing data got updated
186+ let expected: Vec<Option<String>> = vec![None, None];
188+ .query("SELECT name FROM users ORDER BY id", &[],)
191+ .map(|row| row.get::<_, Option<String>>("name"))
194+ // Insert data using old schema and ensure new column is NULL
196+ .simple_query("INSERT INTO users (id) VALUES (3)")
198+ let name: Option<String> = new_db
199+ .query_one("SELECT name from users WHERE id = 3", &[])
200+ .map(|row| (row.get("name")))
202+ assert_eq!(None, name);
204+ // Ensure data can be inserted against new schema
206+ .simple_query("INSERT INTO users (id, name) VALUES (3, 'Test Testsson'), (4, NULL)")
209+ reshape.complete_migration().unwrap();
213+fn add_column_with_default() {
214+ let (mut reshape, mut old_db, mut new_db) = common::setup();
216+ let create_users_table = Migration::new("create_users_table", None).with_action(CreateTable {
217+ name: "users".to_string(),
218+ columns: vec![Column {
219+ name: "id".to_string(),
220+ data_type: "SERIAL".to_string(),
225+ let add_name_column =
226+ Migration::new("add_name_column_with_default", None).with_action(AddColumn {
227+ table: "users".to_string(),
229+ name: "name".to_string(),
230+ data_type: "TEXT".to_string(),
232+ default: Some("'DEFAULT'".to_string()),
237+ let first_migrations = vec![create_users_table.clone()];
238+ let second_migrations = vec![create_users_table.clone(), add_name_column.clone()];
240+ // Run first migration, should automatically finish
241+ reshape.migrate(first_migrations.clone()).unwrap();
242+ assert!(matches!(reshape.state.status, Status::Idle));
244+ Some(&create_users_table.name),
245+ reshape.state.current_migration.as_ref()
248+ // Update search paths
250+ .simple_query(&reshape::generate_schema_query(
251+ &first_migrations.last().unwrap().name,
255+ .simple_query(&reshape::generate_schema_query(
256+ &first_migrations.last().unwrap().name,
260+ // Insert some test values
262+ .simple_query("INSERT INTO users (id) VALUES (1), (2)")
265+ // Run second migration
266+ reshape.migrate(second_migrations.clone()).unwrap();
268+ .simple_query(&reshape::generate_schema_query(
269+ &second_migrations.last().unwrap().name,
273+ // Ensure existing data got updated with defaults
274+ let expected = vec!["DEFAULT".to_string(), "DEFAULT".to_string()];
276+ .query("SELECT name FROM users ORDER BY id", &[],)
279+ .map(|row| row.get::<_, String>("name"))
282+ // Insert data using old schema and ensure new column gets the default value
284+ .simple_query("INSERT INTO users (id) VALUES (3)")
286+ let name: String = new_db
287+ .query_one("SELECT name from users WHERE id = 3", &[])
288+ .map(|row| row.get("name"))
290+ assert_eq!("DEFAULT", name);
292+ reshape.complete_migration().unwrap();