#![cfg(feature = "std")]
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn load(bin: &str, db: &str, sql: &str) {
let _ = std::fs::remove_file(db);
let out = Command::new(bin)
.arg(db)
.arg(sql)
.output()
.expect("run binary");
assert!(
out.status.success(),
"load failed: {}",
String::from_utf8_lossy(&out.stderr)
);
}
fn query(bin: &str, db: &str, sql: &str) -> String {
let out = Command::new(bin).arg(db).arg(sql).output().expect("query");
String::from_utf8_lossy(&out.stdout).trim_end().to_owned()
}
fn tmp(name: &str) -> String {
let mut p = std::env::temp_dir();
p.push(format!("graphite_irc_{}_{}.db", std::process::id(), name));
p.to_string_lossy().into_owned()
}
fn replace_script(base: usize, n: usize, sz: usize) -> String {
format!(
"PRAGMA page_size=4096;\
CREATE TABLE t(k TEXT PRIMARY KEY, v BLOB);\
WITH RECURSIVE c(x) AS(SELECT 1 UNION ALL SELECT x+1 FROM c WHERE x<{base})\
INSERT INTO t SELECT 'key'||x, zeroblob(120) FROM c;\
INSERT OR REPLACE INTO t \
SELECT 'key'||x, zeroblob({sz}) \
FROM (WITH RECURSIVE c(x) AS(SELECT 1 UNION ALL SELECT x+1 FROM c WHERE x<{n}) SELECT x FROM c);"
)
}
fn assert_valid_and_matching(name: &str, sql: &str) {
let g = env!("CARGO_BIN_EXE_graphitesql");
let gdb = tmp(&format!("g_{name}"));
load(g, &gdb, sql);
let integ = query(g, &gdb, "PRAGMA integrity_check");
assert_eq!(integ, "ok", "[{name}] graphite integrity_check: {integ}");
if sqlite3_available() {
let qc = query("sqlite3", &gdb, "PRAGMA quick_check");
assert_eq!(
qc, "ok",
"[{name}] sqlite3 quick_check on graphite file: {qc}"
);
let sdb = tmp(&format!("s_{name}"));
load("sqlite3", &sdb, sql);
let probe = "SELECT k, length(v) FROM t ORDER BY k";
let gr = query(g, &gdb, probe);
let sr = query("sqlite3", &sdb, probe);
assert_eq!(gr, sr, "[{name}] row set diverged from sqlite3");
let _ = std::fs::remove_file(&sdb);
}
let _ = std::fs::remove_file(&gdb);
}
#[test]
fn insert_or_replace_growing_rows_keeps_btree_valid() {
assert_valid_and_matching("r50_6k", &replace_script(300, 50, 6000));
assert_valid_and_matching("r150_6k", &replace_script(300, 150, 6000));
assert_valid_and_matching("r150_2k", &replace_script(300, 150, 2000));
assert_valid_and_matching("r299_4k", &replace_script(300, 299, 4000));
}
#[test]
fn insert_or_replace_mixed_cache_workload_stays_valid() {
let sql = "PRAGMA auto_vacuum=FULL;\
PRAGMA page_size=4096;\
CREATE TABLE cache(k TEXT PRIMARY KEY, v BLOB);\
WITH RECURSIVE c(x) AS(SELECT 1 UNION ALL SELECT x+1 FROM c WHERE x<1200)\
INSERT INTO cache(k,v) SELECT 'key'||x, zeroblob(100+x*5) FROM c;\
DELETE FROM cache WHERE k IN(SELECT k FROM cache WHERE rowid%2=0);\
INSERT OR REPLACE INTO cache(k,v) \
SELECT 'key'||x, zeroblob(6000) \
FROM (WITH RECURSIVE c(x) AS(SELECT 1 UNION ALL SELECT x+1 FROM c WHERE x<600) SELECT x FROM c);\
DELETE FROM cache WHERE length(v)<500;";
let g = env!("CARGO_BIN_EXE_graphitesql");
let gdb = tmp("g_cache");
load(g, &gdb, sql);
let integ = query(g, &gdb, "PRAGMA integrity_check");
assert_eq!(integ, "ok", "graphite integrity_check: {integ}");
if sqlite3_available() {
let qc = query("sqlite3", &gdb, "PRAGMA quick_check");
assert_eq!(qc, "ok", "sqlite3 quick_check on graphite file: {qc}");
let sdb = tmp("s_cache");
load("sqlite3", &sdb, sql);
let probe = "SELECT k, length(v) FROM cache ORDER BY k";
assert_eq!(
query(g, &gdb, probe),
query("sqlite3", &sdb, probe),
"cache row set diverged from sqlite3"
);
let _ = std::fs::remove_file(&sdb);
}
let _ = std::fs::remove_file(&gdb);
}