#![cfg(all(feature = "native", unix))]
use fsqlite::Connection;
use fsqlite_types::SqliteValue;
fn build_stock_database(dir: &tempfile::TempDir) -> std::path::PathBuf {
let path = dir.path().join("gh292.db");
let conn = rusqlite::Connection::open(&path).expect("create stock database");
conn.execute_batch(
"PRAGMA journal_mode=WAL;
CREATE TABLE probe(id INTEGER PRIMARY KEY, value TEXT NOT NULL);
INSERT INTO probe(value) VALUES ('alpha'), ('beta');
PRAGMA wal_checkpoint(TRUNCATE);",
)
.expect("seed and checkpoint stock database");
drop(conn);
path
}
async fn assert_fsqlite_reads_rows(path: &std::path::Path) {
let conn = Connection::open(path.to_str().unwrap())
.await
.expect("open must treat the unusable WAL sidecar as empty (GH #292)");
let rows = conn
.query("SELECT value FROM probe ORDER BY id")
.await
.expect("query rows from the main database");
let values: Vec<_> = rows.iter().map(|row| row.values()[0].clone()).collect();
assert_eq!(
values,
vec![
SqliteValue::Text("alpha".to_owned().into()),
SqliteValue::Text("beta".to_owned().into()),
]
);
}
#[test]
fn open_treats_garbage_wal_sidecar_as_empty() {
asupersync::test_utils::run_test(|| async {
let dir = tempfile::TempDir::new().unwrap();
let path = build_stock_database(&dir);
let wal_path = dir.path().join("gh292.db-wal");
std::fs::write(&wal_path, vec![0xA5_u8; 4096]).expect("plant garbage WAL sidecar");
let stock = rusqlite::Connection::open(&path).expect("stock open");
let ok: String = stock
.query_row("PRAGMA integrity_check", [], |row| row.get(0))
.expect("stock integrity_check");
assert_eq!(
ok, "ok",
"oracle: stock SQLite treats the database as healthy"
);
drop(stock);
std::fs::write(&wal_path, vec![0xA5_u8; 4096]).expect("re-plant garbage WAL sidecar");
assert_fsqlite_reads_rows(&path).await;
});
}
#[test]
fn open_treats_checksum_flipped_wal_header_as_empty() {
asupersync::test_utils::run_test(|| async {
let dir = tempfile::TempDir::new().unwrap();
let path = build_stock_database(&dir);
let wal_path = dir.path().join("gh292.db-wal");
let mut header = vec![0_u8; 32];
header[..4].copy_from_slice(&0x377f_0682_u32.to_be_bytes()); header[4..8].copy_from_slice(&3_007_000_u32.to_be_bytes()); header[8..12].copy_from_slice(&4096_u32.to_be_bytes()); header[24..28].copy_from_slice(&0xDEAD_BEEF_u32.to_be_bytes()); std::fs::write(&wal_path, header).expect("plant checksum-mismatched WAL header");
let stock = rusqlite::Connection::open(&path).expect("stock open with bad-checksum WAL");
let n: i64 = stock
.query_row("SELECT COUNT(*) FROM probe", [], |row| row.get(0))
.expect("stock read");
assert_eq!(n, 2);
drop(stock);
let mut header = vec![0_u8; 32];
header[..4].copy_from_slice(&0x377f_0682_u32.to_be_bytes());
header[4..8].copy_from_slice(&3_007_000_u32.to_be_bytes());
header[8..12].copy_from_slice(&4096_u32.to_be_bytes());
header[24..28].copy_from_slice(&0xDEAD_BEEF_u32.to_be_bytes());
std::fs::write(&wal_path, header).expect("re-plant checksum-mismatched WAL header");
assert_fsqlite_reads_rows(&path).await;
});
}