Skip to content

Commit d1c49d1

Browse files
committed
Add schema migrations and test them
1 parent 259057c commit d1c49d1

File tree

2 files changed

+113
-4
lines changed

2 files changed

+113
-4
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use rusqlite::Connection;
2+
3+
pub(super) fn migrate_schema(connection: &Connection, kv_table_name: &str, from_version: u16, to_version: u16) {
4+
assert!(from_version < to_version);
5+
if from_version == 1 && to_version == 2 {
6+
let sql = format!(
7+
"ALTER TABLE {}
8+
ADD sub_namespace TEXT DEFAULT \"\" NOT NULL;",
9+
kv_table_name);
10+
connection
11+
.execute(&sql, [])
12+
.unwrap_or_else(|e| panic!("Failed to migrate table {} from user_version {} to {}: {}", kv_table_name, from_version, to_version, e));
13+
}
14+
}
15+
16+
#[cfg(test)]
17+
mod tests {
18+
use crate::sqlite_store::SqliteStore;
19+
use crate::test_utils::do_read_write_remove_list_persist;
20+
21+
use rusqlite::{named_params, Connection};
22+
23+
use std::fs;
24+
25+
#[test]
26+
fn rwrl_post_schema_1_migration() {
27+
let old_schema_version = 1;
28+
29+
let mut temp_path = std::env::temp_dir();
30+
temp_path.push("rwrl_post_schema_1_migration");
31+
32+
let db_file_name = "test_db".to_string();
33+
let kv_table_name = "test_table".to_string();
34+
35+
let test_namespace = "testspace".to_string();
36+
let test_key = "testkey".to_string();
37+
let test_data = [42u8; 32];
38+
39+
{
40+
// We create a database with a SCHEMA_VERSION 1 table
41+
fs::create_dir_all(temp_path.clone()).unwrap();
42+
let mut db_file_path = temp_path.clone();
43+
db_file_path.push(db_file_name.clone());
44+
45+
let connection = Connection::open(db_file_path.clone()).unwrap();
46+
47+
connection
48+
.pragma(Some(rusqlite::DatabaseName::Main), "user_version", old_schema_version, |_| {
49+
Ok(())
50+
}).unwrap();
51+
52+
let sql = format!(
53+
"CREATE TABLE IF NOT EXISTS {} (
54+
namespace TEXT NOT NULL,
55+
key TEXT NOT NULL CHECK (key <> ''),
56+
value BLOB, PRIMARY KEY ( namespace, key )
57+
);",
58+
kv_table_name
59+
);
60+
61+
connection.execute(&sql, []).unwrap();
62+
63+
// We write some data to to the table
64+
let sql = format!(
65+
"INSERT OR REPLACE INTO {} (namespace, key, value) VALUES (:namespace, :key, :value);",
66+
kv_table_name
67+
);
68+
let mut stmt = connection.prepare_cached(&sql).unwrap();
69+
70+
stmt.execute(
71+
named_params! {
72+
":namespace": test_namespace,
73+
":key": test_key,
74+
":value": test_data,
75+
}).unwrap();
76+
77+
// We read the just written data back to assert it happened.
78+
let sql = format!("SELECT value FROM {} WHERE namespace=:namespace AND key=:key;",
79+
kv_table_name);
80+
let mut stmt = connection.prepare_cached(&sql).unwrap();
81+
82+
let res: Vec<u8> = stmt
83+
.query_row(
84+
named_params! {
85+
":namespace": test_namespace,
86+
":key": test_key,
87+
},
88+
|row| row.get(0),
89+
).unwrap();
90+
91+
assert_eq!(res, test_data);
92+
}
93+
94+
let store = SqliteStore::new(temp_path, Some(db_file_name), Some(kv_table_name));
95+
do_read_write_remove_list_persist(&store);
96+
}
97+
}

lightning-persister/src/sqlite_store/mod.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use std::fs;
1010
use std::path::PathBuf;
1111
use std::sync::{Arc, Mutex};
1212

13+
mod migrations;
14+
1315
/// The default database file name.
1416
pub const DEFAULT_SQLITE_DB_FILE_NAME: &str = "ldk_data.sqlite";
1517

@@ -49,11 +51,21 @@ impl SqliteStore {
4951
panic!("Failed to open/create database file: {}", db_file_path.display())
5052
});
5153

52-
connection
53-
.pragma(Some(rusqlite::DatabaseName::Main), "user_version", SCHEMA_USER_VERSION, |_| {
54-
Ok(())
55-
})
54+
let sql = format!("SELECT user_version FROM pragma_user_version");
55+
let version_res: u16 = connection.query_row(&sql, [], |row| row.get(0)).unwrap();
56+
57+
if version_res == 0 {
58+
// New database, set our SCHEMA_USER_VERSION and continue
59+
connection
60+
.pragma(Some(rusqlite::DatabaseName::Main), "user_version", SCHEMA_USER_VERSION, |_| {
61+
Ok(())
62+
})
5663
.unwrap_or_else(|_| panic!("Failed to set PRAGMA user_version"));
64+
} else if version_res < SCHEMA_USER_VERSION {
65+
migrations::migrate_schema(&connection, &kv_table_name, version_res, SCHEMA_USER_VERSION);
66+
} else if version_res > SCHEMA_USER_VERSION {
67+
panic!("Failed to open database: incompatible schema version {}. Expected: {}", version_res, SCHEMA_USER_VERSION);
68+
}
5769

5870
let sql = format!(
5971
"CREATE TABLE IF NOT EXISTS {} (

0 commit comments

Comments
 (0)