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
use rusqlite;
use tolstoy_traits::errors::Result;
pub static REMOTE_HEAD_KEY: &str = r#"remote_head"#;
lazy_static! {
#[cfg_attr(rustfmt, rustfmt_skip)]
static ref SCHEMA_STATEMENTS: Vec<&'static str> = { vec![
r#"CREATE TABLE IF NOT EXISTS tolstoy_tu (tx INTEGER PRIMARY KEY, uuid BLOB NOT NULL UNIQUE) WITHOUT ROWID"#,
r#"CREATE TABLE IF NOT EXISTS tolstoy_metadata (key BLOB NOT NULL UNIQUE, value BLOB NOT NULL)"#,
r#"CREATE INDEX IF NOT EXISTS idx_tolstoy_tu_ut ON tolstoy_tu (uuid, tx)"#,
]
};
}
pub fn ensure_current_version(conn: &mut rusqlite::Connection) -> Result<()> {
let tx = conn.transaction()?;
for statement in (&SCHEMA_STATEMENTS).iter() {
tx.execute(statement, &[])?;
}
tx.execute("INSERT OR IGNORE INTO tolstoy_metadata (key, value) VALUES (?, zeroblob(16))", &[&REMOTE_HEAD_KEY])?;
tx.commit().map_err(|e| e.into())
}
#[cfg(test)]
pub mod tests {
use super::*;
use uuid::Uuid;
fn setup_conn_bare() -> rusqlite::Connection {
let conn = rusqlite::Connection::open_in_memory().unwrap();
conn.execute_batch("
PRAGMA page_size=32768;
PRAGMA journal_mode=wal;
PRAGMA wal_autocheckpoint=32;
PRAGMA journal_size_limit=3145728;
PRAGMA foreign_keys=ON;
").expect("success");
conn
}
pub fn setup_conn() -> rusqlite::Connection {
let mut conn = setup_conn_bare();
ensure_current_version(&mut conn).expect("connection setup");
conn
}
#[test]
fn test_empty() {
let mut conn = setup_conn_bare();
assert!(ensure_current_version(&mut conn).is_ok());
let mut stmt = conn.prepare("SELECT key FROM tolstoy_metadata WHERE value = zeroblob(16)").unwrap();
let mut keys_iter = stmt.query_map(&[], |r| r.get(0)).expect("query works");
let first: Result<String> = keys_iter.next().unwrap().map_err(|e| e.into());
let second: Option<_> = keys_iter.next();
match (first, second) {
(Ok(key), None) => {
assert_eq!(key, REMOTE_HEAD_KEY);
},
(_, _) => { panic!("Wrong number of results."); },
}
}
#[test]
fn test_non_empty() {
let mut conn = setup_conn_bare();
assert!(ensure_current_version(&mut conn).is_ok());
let test_uuid = Uuid::new_v4();
{
let tx = conn.transaction().unwrap();
let uuid_bytes = test_uuid.as_bytes().to_vec();
match tx.execute("UPDATE tolstoy_metadata SET value = ? WHERE key = ?", &[&uuid_bytes, &REMOTE_HEAD_KEY]) {
Err(e) => panic!("Error running an update: {}", e),
_ => ()
}
match tx.commit() {
Err(e) => panic!("Error committing an update: {}", e),
_ => ()
}
}
assert!(ensure_current_version(&mut conn).is_ok());
let mut stmt = conn.prepare("SELECT value FROM tolstoy_metadata").unwrap();
let mut values_iter = stmt.query_map(&[], |r| {
let raw_uuid: Vec<u8> = r.get(0);
Uuid::from_bytes(raw_uuid.as_slice()).unwrap()
}).expect("query works");
let first: Result<Uuid> = values_iter.next().unwrap().map_err(|e| e.into());
let second: Option<_> = values_iter.next();
match (first, second) {
(Ok(uuid), None) => {
assert_eq!(test_uuid, uuid);
},
(_, _) => { panic!("Wrong number of results."); },
}
}
}