Skip to content

Commit 85874f5

Browse files
authored
Enable WAL mode for extension connections (#11)
1 parent 8f2f6c9 commit 85874f5

2 files changed

Lines changed: 57 additions & 1 deletion

File tree

absurd-sqlite-extension/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ fn absurd_init(db: *mut sqlite3) -> Result<()> {
6868
"jsonb() requires SQLite 3.45.0+; please upgrade SQLite",
6969
));
7070
}
71+
sql::ensure_wal_journal_mode(db)?;
7172
let flags = FunctionFlags::UTF8 | FunctionFlags::DETERMINISTIC;
7273
define_scalar_function(db, "absurd_version", 0, absurd_version, flags)?;
7374
define_scalar_function(db, "absurd_create_queue", 1, absurd_create_queue, flags)?;
@@ -229,6 +230,7 @@ mod tests {
229230
use super::*;
230231
use rusqlite::{ffi::sqlite3_auto_extension, params, Connection};
231232
use std::collections::HashMap;
233+
use uuid::Uuid;
232234

233235
#[test]
234236
fn test_absurd_version() {
@@ -1231,4 +1233,21 @@ mod tests {
12311233
.unwrap();
12321234
assert_eq!(run_state, "cancelled");
12331235
}
1236+
1237+
#[test]
1238+
fn test_enables_wal_journal_mode_for_file_db() {
1239+
unsafe {
1240+
sqlite3_auto_extension(Some(std::mem::transmute(sqlite3_absurd_init as *const ())));
1241+
}
1242+
1243+
let db_path = std::env::temp_dir().join(format!("absurd-wal-{}.db", Uuid::new_v4()));
1244+
let conn = Connection::open(&db_path).unwrap();
1245+
1246+
let journal_mode: String = conn
1247+
.query_row("pragma journal_mode", [], |row| row.get(0))
1248+
.unwrap();
1249+
assert_eq!(journal_mode.to_lowercase(), "wal");
1250+
1251+
let _ = std::fs::remove_file(db_path);
1252+
}
12341253
}

absurd-sqlite-extension/src/sql.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use sqlite_loadable::ext::{
33
sqlite3ext_bind_text, sqlite3ext_finalize, sqlite3ext_prepare_v2, sqlite3ext_step,
44
};
55
use sqlite_loadable::{Error, Result, SQLITE_DONE, SQLITE_OKAY, SQLITE_ROW};
6-
use std::ffi::CString;
6+
use std::ffi::{CStr, CString};
77
use std::os::raw::{c_char, c_void};
88
use std::time::{SystemTime, UNIX_EPOCH};
99

@@ -174,3 +174,40 @@ pub fn now_ms_from_db(db: *mut sqlite3) -> i64 {
174174

175175
now_ms()
176176
}
177+
178+
pub fn ensure_wal_journal_mode(db: *mut sqlite3) -> Result<()> {
179+
let mut stmt = sqlite_loadable::exec::Statement::prepare(db, "pragma journal_mode=WAL")
180+
.map_err(|err| Error::new_message(format!("failed to enable WAL mode: {:?}", err)))?;
181+
let mut rows = stmt.execute();
182+
let row = match rows.next() {
183+
Some(Ok(row)) => row,
184+
_ => {
185+
return Err(Error::new_message(
186+
"failed to read journal_mode after enabling WAL",
187+
))
188+
}
189+
};
190+
let mode = row
191+
.get::<String>(0)
192+
.map_err(|err| Error::new_message(format!("failed to read journal_mode: {:?}", err)))?;
193+
if mode.eq_ignore_ascii_case("wal") || db_is_in_memory(db) {
194+
return Ok(());
195+
}
196+
Err(Error::new_message(format!(
197+
"expected journal_mode to be WAL, found {}",
198+
mode
199+
)))
200+
}
201+
202+
fn db_is_in_memory(db: *mut sqlite3) -> bool {
203+
let main = CString::new("main").unwrap();
204+
let filename_ptr = unsafe { sqlite3ext_sys::sqlite3_db_filename(db, main.as_ptr()) };
205+
if filename_ptr.is_null() {
206+
return true;
207+
}
208+
let filename = unsafe { CStr::from_ptr(filename_ptr) }
209+
.to_string_lossy()
210+
.into_owned();
211+
let trimmed = filename.trim();
212+
trimmed.is_empty() || trimmed == ":memory:"
213+
}

0 commit comments

Comments
 (0)