#![cfg(feature = "std")]
use graphitesql::{Connection, Value};
use std::process::Command;
fn have_sqlite3() -> bool {
Command::new("sqlite3")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn sqlite3(path: &str, sql: &str) -> String {
let out = Command::new("sqlite3")
.arg(path)
.arg(sql)
.output()
.expect("spawn sqlite3");
assert!(
out.status.success(),
"sqlite3 {sql:?} failed: {}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
fn tmp_path(tag: &str) -> String {
let mut p = std::env::temp_dir();
p.push(format!("graphitesql-avw-{}-{}.db", tag, std::process::id()));
let path = p.to_string_lossy().into_owned();
let _ = std::fs::remove_file(&path);
let _ = std::fs::remove_file(format!("{path}-journal"));
path
}
fn cleanup(path: &str) {
let _ = std::fs::remove_file(path);
let _ = std::fs::remove_file(format!("{path}-journal"));
}
#[test]
fn full_auto_vacuum_roundtrips_through_sqlite() {
let path = tmp_path("full");
let big: String = "Z".repeat(4500);
let kept: i64 = {
let mut c = Connection::create(&path).unwrap();
c.execute("PRAGMA auto_vacuum=FULL").unwrap();
assert_eq!(
c.query("PRAGMA auto_vacuum").unwrap().rows[0][0],
Value::Integer(1),
"graphite should report FULL after setting it on an empty db"
);
c.execute("CREATE TABLE t(a INTEGER PRIMARY KEY, b TEXT)")
.unwrap();
for i in 1..=1000i64 {
c.execute(&format!("INSERT INTO t VALUES({i}, '{big}')"))
.unwrap();
}
let huge: String = "Q".repeat(50_000);
c.execute(&format!("INSERT INTO t VALUES(100000, '{huge}')"))
.unwrap();
c.execute("CREATE INDEX ix ON t(b)").unwrap();
c.execute("DELETE FROM t WHERE a <= 200").unwrap();
let kept = match c.query("SELECT count(*) FROM t").unwrap().rows[0][0] {
Value::Integer(n) => n,
ref v => panic!("count not an integer: {v:?}"),
};
assert_eq!(kept, 801);
kept
};
{
let c = Connection::open(&path).unwrap();
assert_eq!(
c.query("PRAGMA auto_vacuum").unwrap().rows[0][0],
Value::Integer(1)
);
assert_eq!(
c.query("SELECT count(*) FROM t").unwrap().rows[0][0],
Value::Integer(kept)
);
assert_eq!(
c.query("SELECT length(b) FROM t WHERE a=100000")
.unwrap()
.rows[0][0],
Value::Integer(50_000)
);
assert_eq!(
c.query("PRAGMA integrity_check").unwrap().rows[0][0],
Value::Text("ok".into())
);
}
if have_sqlite3() {
assert_eq!(
sqlite3(&path, "PRAGMA integrity_check;"),
"ok",
"sqlite3 integrity_check on a graphite-written FULL auto_vacuum db"
);
assert_eq!(sqlite3(&path, "PRAGMA auto_vacuum;"), "1");
assert_eq!(sqlite3(&path, "SELECT count(*) FROM t;"), kept.to_string());
assert_eq!(
sqlite3(&path, "SELECT length(b) FROM t WHERE a=100000;"),
"50000"
);
assert_eq!(sqlite3(&path, "SELECT count(*) FROM t WHERE b='ZZZ';"), "0");
assert_eq!(
sqlite3(
&path,
&format!("SELECT a FROM t WHERE b='{big}' AND a=777;")
),
"777"
);
}
cleanup(&path);
}
#[test]
fn incremental_auto_vacuum_roundtrips_through_sqlite() {
let path = tmp_path("incr");
let kept: i64 = {
let mut c = Connection::create(&path).unwrap();
c.execute("PRAGMA auto_vacuum=INCREMENTAL").unwrap();
assert_eq!(
c.query("PRAGMA auto_vacuum").unwrap().rows[0][0],
Value::Integer(2)
);
c.execute("CREATE TABLE t(a INTEGER PRIMARY KEY, b TEXT)")
.unwrap();
for i in 1..=800i64 {
c.execute(&format!("INSERT INTO t VALUES({i}, 'row-{i}')"))
.unwrap();
}
c.execute("CREATE INDEX ix ON t(b)").unwrap();
c.execute("DELETE FROM t WHERE a % 5 = 0").unwrap();
match c.query("SELECT count(*) FROM t").unwrap().rows[0][0] {
Value::Integer(n) => n,
ref v => panic!("count: {v:?}"),
}
};
if have_sqlite3() {
assert_eq!(sqlite3(&path, "PRAGMA integrity_check;"), "ok");
assert_eq!(sqlite3(&path, "PRAGMA auto_vacuum;"), "2");
assert_eq!(sqlite3(&path, "SELECT count(*) FROM t;"), kept.to_string());
}
cleanup(&path);
}
#[test]
fn auto_vacuum_pragma_is_noop_on_nonempty_db() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE t(a)").unwrap();
c.execute("INSERT INTO t VALUES(1)").unwrap();
c.execute("PRAGMA auto_vacuum=FULL").unwrap();
assert_eq!(
c.query("PRAGMA auto_vacuum").unwrap().rows[0][0],
Value::Integer(0)
);
c.execute("INSERT INTO t VALUES(2)").unwrap();
assert_eq!(
c.query("SELECT count(*) FROM t").unwrap().rows[0][0],
Value::Integer(2)
);
}