#![cfg(feature = "std")]
use graphitesql::{Connection, Value};
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn render(v: &Value) -> String {
match v {
Value::Null => String::new(),
Value::Integer(i) => i.to_string(),
Value::Text(s) => s.clone(),
Value::Real(r) => graphitesql::exec::eval::format_real(*r),
Value::Blob(b) => b.iter().map(|x| format!("{x:02x}")).collect(),
}
}
fn graphite(c: &Connection, sql: &str) -> String {
c.query(sql)
.unwrap()
.rows
.iter()
.map(|row| row.iter().map(render).collect::<Vec<_>>().join("|"))
.collect::<Vec<_>>()
.join("\n")
.trim_end()
.to_string()
}
#[test]
fn vdbe_default_matches_sqlite3() {
if !sqlite3_available() {
eprintln!("sqlite3 not found; skipping");
return;
}
let setup = "CREATE TABLE t(id INTEGER PRIMARY KEY, a INT, b TEXT, c REAL);\
INSERT INTO t VALUES(1,5,'x',1.5),(2,3,'y',2.5),(3,5,'x',3.5),(4,NULL,'z',NULL);\
CREATE INDEX ic ON t(c);\
CREATE TABLE u(uid INTEGER PRIMARY KEY, tid INT, w INT);\
INSERT INTO u VALUES(1,1,10),(2,1,20),(3,2,30)";
let path = std::env::temp_dir().join(format!("gsql-vdbedef-{}.db", std::process::id()));
let path = path.to_string_lossy().into_owned();
let _ = std::fs::remove_file(&path);
let out = Command::new("sqlite3")
.arg(&path)
.arg(setup)
.output()
.unwrap();
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stderr)
);
let mut g = Connection::open_memory().unwrap();
for s in setup.split(';') {
if !s.trim().is_empty() {
g.execute(s).unwrap();
}
}
let queries = [
"SELECT hex(x'00' || x'ff'), hex('a' || x'00' || 'b')",
"SELECT -(-9223372036854775808), typeof(-(-9223372036854775808))",
"SELECT a FROM t ORDER BY c",
"SELECT a FROM t ORDER BY c DESC",
"SELECT count(*), sum(a), min(c), max(c) FROM t",
"SELECT b, count(*) FROM t GROUP BY b ORDER BY b",
"SELECT b, sum(a) FROM t GROUP BY b HAVING sum(a) > 3 ORDER BY b",
"SELECT count(*) FROM t HAVING count(*) > 0",
"SELECT b, count(*), a FROM t GROUP BY b ORDER BY b",
"SELECT t.id, u.w FROM t JOIN u ON t.id = u.tid ORDER BY t.id, u.w",
"SELECT id, (SELECT sum(w) FROM u WHERE tid = t.id) FROM t ORDER BY id",
"SELECT a FROM t WHERE a IS NOT NULL UNION SELECT w FROM u ORDER BY 1",
"SELECT DISTINCT a FROM t ORDER BY a",
"SELECT id FROM t WHERE b IN ('x','y') ORDER BY id",
"SELECT coalesce(a,-1), CASE WHEN c IS NULL THEN 'n' ELSE 'y' END FROM t ORDER BY id",
];
let mut failures = Vec::new();
for q in queries {
let want = {
let o = Command::new("sqlite3").arg(&path).arg(q).output().unwrap();
String::from_utf8_lossy(&o.stdout).trim_end().to_string()
};
let got = graphite(&g, q);
if got != want {
failures.push(format!(
" {q}\n sqlite: {want:?}\n graphite: {got:?}"
));
}
}
let _ = std::fs::remove_file(&path);
assert!(
failures.is_empty(),
"{} VDBE-default queries diverged from sqlite3:\n{}",
failures.len(),
failures.join("\n")
);
}
#[test]
fn vdbe_default_rejects_like_sqlite3() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE t(a INT, b INT)").unwrap();
c.execute("INSERT INTO t VALUES(1,2),(3,4)").unwrap();
assert!(c.query("SELECT a FROM t ORDER BY 5").is_err());
assert!(c.query("SELECT a FROM t HAVING a > 0").is_err());
}