-
Notifications
You must be signed in to change notification settings - Fork 456
Expand file tree
/
Copy pathmigrations.rs
More file actions
164 lines (136 loc) · 4.25 KB
/
migrations.rs
File metadata and controls
164 lines (136 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use rusqlite::Connection;
use lightning::io;
pub(super) fn migrate_schema(
connection: &mut Connection, kv_table_name: &str, from_version: u16, to_version: u16,
) -> io::Result<()> {
assert!(from_version < to_version);
if from_version == 1 && to_version == 2 {
let tx = connection.transaction().map_err(|e| {
let msg = format!(
"Failed to migrate table {} from user_version {} to {}: {}",
kv_table_name, from_version, to_version, e
);
io::Error::new(io::ErrorKind::Other, msg)
})?;
// Rename 'namespace' column to 'primary_namespace'
let sql = format!(
"ALTER TABLE {}
RENAME COLUMN namespace TO primary_namespace;",
kv_table_name
);
tx.execute(&sql, []).map_err(|e| {
let msg = format!(
"Failed to migrate table {} from user_version {} to {}: {}",
kv_table_name, from_version, to_version, e
);
io::Error::new(io::ErrorKind::Other, msg)
})?;
// Add new 'secondary_namespace' column
let sql = format!(
"ALTER TABLE {}
ADD secondary_namespace TEXT DEFAULT \"\" NOT NULL;",
kv_table_name
);
tx.execute(&sql, []).map_err(|e| {
let msg = format!(
"Failed to migrate table {} from user_version {} to {}: {}",
kv_table_name, from_version, to_version, e
);
io::Error::new(io::ErrorKind::Other, msg)
})?;
// Update user_version
tx.pragma(Some(rusqlite::DatabaseName::Main), "user_version", to_version, |_| Ok(()))
.map_err(|e| {
let msg = format!(
"Failed to upgrade user_version from {} to {}: {}",
from_version, to_version, e
);
io::Error::new(io::ErrorKind::Other, msg)
})?;
tx.commit().map_err(|e| {
let msg = format!(
"Failed to migrate table {} from user_version {} to {}: {}",
kv_table_name, from_version, to_version, e
);
io::Error::new(io::ErrorKind::Other, msg)
})?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use crate::sqlite_store::SqliteStore;
use crate::test_utils::{do_read_write_remove_list_persist, random_storage_path};
use lightning::util::persist::KVStore;
use rusqlite::{named_params, Connection};
use std::fs;
#[test]
fn rwrl_post_schema_1_migration() {
let old_schema_version = 1;
let mut temp_path = random_storage_path();
temp_path.push("rwrl_post_schema_1_migration");
let db_file_name = "test_db".to_string();
let kv_table_name = "test_table".to_string();
let test_namespace = "testspace".to_string();
let test_key = "testkey".to_string();
let test_data = [42u8; 32];
{
// We create a database with a SCHEMA_VERSION 1 table
fs::create_dir_all(temp_path.clone()).unwrap();
let mut db_file_path = temp_path.clone();
db_file_path.push(db_file_name.clone());
let connection = Connection::open(db_file_path.clone()).unwrap();
connection
.pragma(
Some(rusqlite::DatabaseName::Main),
"user_version",
old_schema_version,
|_| Ok(()),
)
.unwrap();
let sql = format!(
"CREATE TABLE IF NOT EXISTS {} (
namespace TEXT NOT NULL,
key TEXT NOT NULL CHECK (key <> ''),
value BLOB, PRIMARY KEY ( namespace, key )
);",
kv_table_name
);
connection.execute(&sql, []).unwrap();
// We write some data to to the table
let sql = format!(
"INSERT OR REPLACE INTO {} (namespace, key, value) VALUES (:namespace, :key, :value);",
kv_table_name
);
let mut stmt = connection.prepare_cached(&sql).unwrap();
stmt.execute(named_params! {
":namespace": test_namespace,
":key": test_key,
":value": test_data,
})
.unwrap();
// We read the just written data back to assert it happened.
let sql = format!(
"SELECT value FROM {} WHERE namespace=:namespace AND key=:key;",
kv_table_name
);
let mut stmt = connection.prepare_cached(&sql).unwrap();
let res: Vec<u8> = stmt
.query_row(
named_params! {
":namespace": test_namespace,
":key": test_key,
},
|row| row.get(0),
)
.unwrap();
assert_eq!(res, test_data);
}
// Check we migrate the db just fine without losing our written data.
let store = SqliteStore::new(temp_path, Some(db_file_name), Some(kv_table_name)).unwrap();
let res = store.read(&test_namespace, "", &test_key).unwrap();
assert_eq!(res, test_data);
// Check we can continue to use the store just fine.
do_read_write_remove_list_persist(&store);
}
}