1use crate::migrate::{self, Migrations};
2use rusqlite::Connection;
3use std::path::Path;
4use std::sync::{Arc, Mutex};
5use std::time::Duration;
6
7pub type Db = Arc<Mutex<Connection>>;
8
9pub const DESK_PEN_WAIT: Duration = Duration::from_secs(1);
10
11pub const JOB_PEN_WAIT: Duration = Duration::from_secs(30);
12
13pub const SLOW_SQL_ALARM: Duration = Duration::from_millis(250);
14
15pub fn open(
16 path: &Path,
17 migrations: &Migrations,
18 mark: Option<fn(&str, Duration)>,
19) -> rusqlite::Result<Connection> {
20 let mut connection = Connection::open(path)?;
21 connection.profile(mark);
22 connection.busy_timeout(DESK_PEN_WAIT)?;
23 connection.pragma_update(None, "foreign_keys", "ON")?;
24 connection.pragma_update(None, "journal_mode", "WAL")?;
25 connection.pragma_update(None, "synchronous", "NORMAL")?;
26 connection.pragma_update(None, "cache_size", "-32768")?;
27 connection.pragma_update(None, "mmap_size", "268435456")?;
28 migrate::run(&connection, migrations)?;
29 Ok(connection)
30}
31
32pub fn open_companion(
33 path: &Path,
34 mark: Option<fn(&str, Duration)>,
35) -> rusqlite::Result<Connection> {
36 let mut connection = Connection::open(path)?;
37 connection.profile(mark);
38 connection.busy_timeout(JOB_PEN_WAIT)?;
39 connection.pragma_update(None, "foreign_keys", "ON")?;
40 connection.pragma_update(None, "journal_mode", "WAL")?;
41 connection.pragma_update(None, "synchronous", "NORMAL")?;
42 Ok(connection)
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use crate::migrate::{Migrations, Step};
49 use std::path::PathBuf;
50
51 fn scratch(tag: &str) -> tempfile::TempDir {
52 let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tmp/testscratch");
53 std::fs::create_dir_all(&root).unwrap();
54 tempfile::Builder::new()
55 .prefix(tag)
56 .tempdir_in(root)
57 .unwrap()
58 }
59
60 #[test]
61 fn the_pen_waits_and_slow_alarm_keep_the_estate_ruling() {
62 assert_eq!(DESK_PEN_WAIT, Duration::from_secs(1));
63 assert_eq!(JOB_PEN_WAIT, Duration::from_secs(30));
64 assert_eq!(SLOW_SQL_ALARM, Duration::from_millis(250));
65 }
66
67 #[test]
68 fn open_applies_the_desk_pragmas_and_migrates() {
69 static STEPS: &[Step] = &[Step::new(1, "CREATE TABLE desk(id INTEGER);", None)];
70 let migrations = Migrations {
71 version: 1,
72 steps: STEPS,
73 before: None,
74 };
75 let holder = scratch("open-pragmas-");
76 let connection = open(&holder.path().join("desk.db"), &migrations, None).unwrap();
77 assert_eq!(pragma::<String>(&connection, "journal_mode"), "wal");
78 assert_eq!(pragma::<i64>(&connection, "foreign_keys"), 1);
79 assert_eq!(pragma::<i64>(&connection, "synchronous"), 1);
80 assert_eq!(pragma::<i64>(&connection, "cache_size"), -32_768);
81 assert_eq!(pragma::<i64>(&connection, "mmap_size"), 268_435_456);
82 }
83
84 #[test]
85 fn open_companion_never_migrates() {
86 let holder = scratch("companion-no-migrate-");
87 let connection = open_companion(&holder.path().join("desk.db"), None).unwrap();
88 assert!(
89 connection
90 .query_row("SELECT value FROM schema_meta", [], |row| row
91 .get::<_, String>(0))
92 .is_err()
93 );
94 }
95
96 fn pragma<T: rusqlite::types::FromSql>(connection: &Connection, name: &str) -> T {
97 connection
98 .query_row(&format!("PRAGMA {name}"), [], |row| row.get(0))
99 .unwrap()
100 }
101}