mempill_sqlite/
connection.rs1use rusqlite::{Connection, Result as SqlResult};
22
23use crate::migrations;
24
25pub fn open(path: &str) -> Result<Connection, crate::SqliteStoreError> {
30 let conn = Connection::open(path)?;
31 apply_pragmas(&conn)?;
32 migrations::apply_migrations(&conn)?;
33 Ok(conn)
34}
35
36pub fn open_in_memory() -> Result<Connection, crate::SqliteStoreError> {
41 let conn = Connection::open_in_memory()?;
42 apply_pragmas(&conn)?;
43 migrations::apply_migrations(&conn)?;
44 Ok(conn)
45}
46
47fn apply_pragmas(conn: &Connection) -> SqlResult<()> {
52 conn.execute_batch(
55 "PRAGMA journal_mode = WAL;\
56 PRAGMA synchronous = FULL;\
57 PRAGMA foreign_keys = ON;",
58 )?;
59 Ok(())
60}
61
62#[cfg(test)]
65mod tests {
66 use super::*;
67 use std::fs;
68
69 fn pragma_str(conn: &Connection, pragma: &str) -> String {
71 conn.query_row(
72 &format!("PRAGMA {pragma}"),
73 [],
74 |row| row.get::<_, String>(0),
75 )
76 .unwrap_or_else(|_| String::new())
77 }
78
79 fn pragma_int(conn: &Connection, pragma: &str) -> i64 {
81 conn.query_row(
82 &format!("PRAGMA {pragma}"),
83 [],
84 |row| row.get::<_, i64>(0),
85 )
86 .unwrap_or(-1)
87 }
88
89 #[test]
94 fn in_memory_synchronous_is_full() {
95 let conn = open_in_memory().expect("in-memory open should succeed");
96 let sync_val = pragma_int(&conn, "synchronous");
97 assert_eq!(sync_val, 2, "synchronous must be FULL (2) on in-memory connection");
98 }
99
100 #[test]
101 fn in_memory_foreign_keys_is_on() {
102 let conn = open_in_memory().expect("in-memory open should succeed");
103 let fk_val = pragma_int(&conn, "foreign_keys");
104 assert_eq!(fk_val, 1, "foreign_keys must be ON (1) on in-memory connection");
105 }
106
107 #[test]
109 fn in_memory_journal_mode_is_memory_not_wal() {
110 let conn = open_in_memory().expect("in-memory open should succeed");
111 let mode = pragma_str(&conn, "journal_mode");
112 assert_eq!(
115 mode, "memory",
116 "in-memory SQLite must use journal_mode=memory (WAL not supported on :memory:)"
117 );
118 }
119
120 #[test]
123 fn file_backed_journal_mode_is_wal() {
124 let dir = tempfile::tempdir().expect("tempdir should create");
125 let path = dir.path().join("test_wal.db");
126 let path_str = path.to_str().unwrap();
127
128 let conn = open(path_str).expect("file-backed open should succeed");
129 let mode = pragma_str(&conn, "journal_mode");
132 assert_eq!(mode, "wal", "file-backed connection must be WAL");
133
134 drop(conn);
135 let _ = fs::remove_file(path.with_extension("db-wal"));
137 let _ = fs::remove_file(path.with_extension("db-shm"));
138 }
139
140 #[test]
141 fn file_backed_synchronous_is_full() {
142 let dir = tempfile::tempdir().expect("tempdir should create");
143 let path = dir.path().join("test_sync.db");
144 let conn = open(path.to_str().unwrap()).expect("file-backed open should succeed");
145 let sync_val = pragma_int(&conn, "synchronous");
146 assert_eq!(sync_val, 2, "synchronous must be FULL (2) on file-backed connection");
148 }
149
150 #[test]
151 fn file_backed_foreign_keys_is_on() {
152 let dir = tempfile::tempdir().expect("tempdir should create");
153 let path = dir.path().join("test_fk.db");
154 let conn = open(path.to_str().unwrap()).expect("file-backed open should succeed");
155 let fk_val = pragma_int(&conn, "foreign_keys");
156 assert_eq!(fk_val, 1, "foreign_keys must be ON (1) on file-backed connection");
157 }
158
159 #[test]
162 fn open_in_memory_runs_migrations() {
163 let conn = open_in_memory().expect("in-memory open should succeed");
164 let count: i64 = conn
166 .query_row(
167 "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='claims'",
168 [],
169 |r| r.get(0),
170 )
171 .unwrap();
172 assert_eq!(count, 1, "claims table must exist after open_in_memory");
173 }
174}