#![cfg(feature = "std")]
use graphitesql::Connection;
use std::process::Command;
fn sqlite3(path: &str, sql: &str) -> String {
let o = Command::new("sqlite3").arg(path).arg(sql).output().unwrap();
String::from_utf8_lossy(&o.stdout).trim_end().to_string()
}
#[test]
fn comparison_affinity_matches_sqlite3() {
if Command::new("sqlite3").arg("--version").output().is_err() {
eprintln!("sqlite3 not found; skipping");
return;
}
let path = std::env::temp_dir().join(format!("gsql-aff-{}.db", std::process::id()));
let path = path.to_string_lossy().into_owned();
let _ = std::fs::remove_file(&path);
let setup = "CREATE TABLE x(none_col, t TEXT, i INTEGER, r REAL);\
INSERT INTO x VALUES (1, '1', 1, 1.0), (5, '5', 5, 5.0);";
sqlite3(&path, setup);
let mut g = Connection::open_memory().unwrap();
for s in setup.split(';') {
if !s.trim().is_empty() {
g.execute(s).unwrap();
}
}
let exprs = [
"none_col = t", "t = none_col", "i = t", "none_col = i", "r = t", "none_col = '1'", "none_col = 1", "t = 1", "t = '1'", "i = '5'", ];
for e in exprs {
let q = format!("SELECT {e} FROM x ORDER BY rowid");
let want = sqlite3(&path, &format!("{q};"));
let got = g
.query(&q)
.unwrap()
.rows
.iter()
.map(|row| match &row[0] {
graphitesql::Value::Null => String::new(),
graphitesql::Value::Integer(i) => i.to_string(),
other => format!("{other:?}"),
})
.collect::<Vec<_>>()
.join("\n");
assert_eq!(got, want, "affinity comparison diverged: {e}");
}
let _ = std::fs::remove_file(&path);
}
#[test]
fn cast_matches_sqlite3() {
if Command::new("sqlite3").arg("--version").output().is_err() {
eprintln!("sqlite3 not found; skipping");
return;
}
let c = Connection::open_memory().unwrap();
for e in [
"CAST(x'3132' AS TEXT)",
"CAST(x'3132' AS INTEGER)",
"CAST(x'3132' AS REAL)",
"CAST(x'3132' AS NUMERIC)",
"CAST('3.0' AS NUMERIC)",
"CAST(2.0 AS NUMERIC)",
"CAST('2e2' AS NUMERIC)",
"CAST('3.5' AS NUMERIC)",
"CAST('abc' AS NUMERIC)",
"CAST('5' AS NUMERIC)",
"CAST(65 AS BLOB)",
"CAST(3.5 AS BLOB)",
"CAST('1.9' AS INTEGER)",
"CAST(-3.9 AS INTEGER)",
"CAST(' 12 ' AS INTEGER)",
"CAST(1e100 AS INTEGER)",
"typeof(CAST('5' AS NUMERIC))",
"typeof(CAST('5.5' AS NUMERIC))",
] {
let q = format!("SELECT quote({e})");
let want = {
let o = Command::new("sqlite3")
.arg(":memory:")
.arg(format!("{q};"))
.output()
.unwrap();
String::from_utf8_lossy(&o.stdout).trim_end().to_string()
};
let got = match &c.query(&q).unwrap().rows[0][0] {
graphitesql::Value::Text(s) => s.clone(),
graphitesql::Value::Null => String::new(),
other => format!("{other:?}"),
};
assert_eq!(got, want, "CAST diverged: {e}");
}
}
#[test]
fn storage_affinity_matches_sqlite3() {
if Command::new("sqlite3").arg("--version").output().is_err() {
eprintln!("sqlite3 not found; skipping");
return;
}
let path = std::env::temp_dir().join(format!("gsql-storeaff-{}.db", std::process::id()));
let path = path.to_string_lossy().into_owned();
let _ = std::fs::remove_file(&path);
let setup =
"CREATE TABLE x(id INTEGER PRIMARY KEY, i INTEGER, r REAL, n NUMERIC, t TEXT, b BLOB);\
INSERT INTO x(i,r,n,t,b) VALUES ('123','4.5','6',7,8.5);\
INSERT INTO x(i,r,n,t,b) VALUES (9.0,9,'10.0',11.5,'12');\
INSERT INTO x(i,r,n,t,b) VALUES ('x','y','3.14','z','w');\
INSERT INTO x(i,r,n,t,b) VALUES (NULL,NULL,'2.0e2',NULL,NULL);";
sqlite3(&path, setup);
let mut g = Connection::open_memory().unwrap();
for s in setup.split(';') {
if !s.trim().is_empty() {
g.execute(s).unwrap();
}
}
for q in [
"SELECT id, typeof(i), typeof(r), typeof(n), typeof(t), typeof(b) FROM x ORDER BY id",
"SELECT id, i, r, n, t, b FROM x ORDER BY id",
"SELECT id FROM x WHERE n = 6 ORDER BY id",
"SELECT id FROM x WHERE i = 123 ORDER BY id",
] {
let want = sqlite3(&path, &format!("{q};"));
let got = render_rows(&g.query(q).unwrap());
assert_eq!(got, want, "storage affinity diverged: {q}");
}
let _ = std::fs::remove_file(&path);
}
fn render_rows(result: &graphitesql::QueryResult) -> String {
result
.rows
.iter()
.map(|row| {
row.iter()
.map(|v| match v {
graphitesql::Value::Null => String::new(),
graphitesql::Value::Integer(i) => i.to_string(),
graphitesql::Value::Text(s) => s.clone(),
graphitesql::Value::Real(r) => graphitesql::exec::eval::format_real(*r),
graphitesql::Value::Blob(b) => b.iter().map(|x| format!("{x:02x}")).collect(),
})
.collect::<Vec<_>>()
.join("|")
})
.collect::<Vec<_>>()
.join("\n")
}