extern "C" {
fn cksumvfs_sqlite_version_number() -> ::std::os::raw::c_int;
fn sqlite3_register_cksumvfs(unused: *const u8) -> ::std::os::raw::c_int;
}
pub fn register_cksumvfs() {
assert_eq!(
unsafe { cksumvfs_sqlite_version_number() as i32 },
rusqlite::version_number()
);
assert_eq!(
unsafe { sqlite3_register_cksumvfs(std::ptr::null()) },
rusqlite::ffi::SQLITE_OK
)
}
pub fn enable_sqlite_checksums(db: &rusqlite::Connection) -> Result<(), anyhow::Error> {
let mut n = 8;
if unsafe {
rusqlite::ffi::sqlite3_file_control(
db.handle(),
std::ptr::null(),
rusqlite::ffi::SQLITE_FCNTL_RESERVE_BYTES,
(&mut n) as *mut i32 as *mut core::ffi::c_void,
)
} != rusqlite::ffi::SQLITE_OK
{
anyhow::bail!("unable to reserve bytes for sqlite3 page checksums");
}
if n != 0 && n != 8 {
anyhow::bail!("database has incorrect reserve bytes for checksums");
}
if n == 0 {
db.execute("vacuum;", [])?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cksumvfs_can_be_enabled() {
let temp_dir = tempfile::tempdir().unwrap();
let path = temp_dir.path().join("test.db3");
{
register_cksumvfs();
let db = rusqlite::Connection::open(&path).unwrap();
enable_sqlite_checksums(&db).unwrap();
let enabled: String = db
.query_row("PRAGMA checksum_verification;", [], |r| {
Ok(r.get(0).unwrap())
})
.unwrap();
assert_eq!(enabled, "1");
}
}
}