#![cfg(feature = "std")]
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn plan(bin: &str, base: &str, sql: &str) -> String {
let full = format!("{base} EXPLAIN QUERY PLAN {sql}");
let out = Command::new(bin)
.arg(":memory:")
.arg(&full)
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
stdout
.lines()
.filter(|l| !l.trim().is_empty() && !l.starts_with("QUERY PLAN"))
.map(|l| l.trim_start_matches(|c: char| " |`*+_-".contains(c)))
.collect::<Vec<_>>()
.join("#")
}
fn raw(bin: &str, base: &str, sql: &str) -> String {
let full = format!("{base} EXPLAIN QUERY PLAN {sql}");
let out = Command::new(bin)
.arg(":memory:")
.arg(&full)
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
if !stdout.trim().is_empty() {
return stdout.trim_end().to_string();
}
String::from_utf8_lossy(&out.stderr).trim_end().to_string()
}
const BASE: &str = "CREATE TABLE t(a,b); CREATE INDEX it ON t(a); CREATE TABLE u(x,y);";
const DATA: &str = "CREATE TABLE t(a,b); CREATE TABLE u(x,y); INSERT INTO t VALUES(1,2),(3,4); \
INSERT INTO u VALUES(3,9),(5,7);";
#[test]
fn compound_body_renders_coroutine() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
for q in [
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u) s",
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u) AS s",
"SELECT * FROM (SELECT a FROM t INTERSECT SELECT x FROM u) s",
"SELECT * FROM (SELECT a FROM t EXCEPT SELECT x FROM u) s",
"WITH c AS (SELECT a FROM t UNION SELECT x FROM u) SELECT * FROM c",
"WITH c AS (SELECT a FROM t INTERSECT SELECT x FROM u) SELECT * FROM c",
"WITH c AS (SELECT a FROM t EXCEPT SELECT x FROM u) SELECT * FROM c",
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u) s ORDER BY 1",
"SELECT DISTINCT * FROM (SELECT a FROM t UNION SELECT x FROM u) s",
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u) s GROUP BY a",
"SELECT max(a) FROM (SELECT a FROM t UNION SELECT x FROM u) s",
"SELECT count(*) FROM (SELECT a FROM t UNION SELECT x FROM u) s",
"WITH c AS (SELECT a FROM t UNION SELECT x FROM u) SELECT DISTINCT a FROM c",
"WITH c AS (SELECT a FROM t INTERSECT SELECT x FROM u) SELECT count(*) FROM c",
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u UNION SELECT a FROM t) s",
"WITH c AS (SELECT a FROM t UNION SELECT x FROM u UNION ALL SELECT a FROM t) SELECT * FROM c",
"SELECT * FROM (SELECT a FROM t UNION ALL SELECT x FROM u UNION SELECT a FROM t) s",
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u LIMIT 3) s",
"SELECT * FROM (SELECT a,b FROM t UNION SELECT x,y FROM u) s",
] {
assert_eq!(plan("sqlite3", BASE, q), plan(g, BASE, q), "for {q}");
}
}
#[test]
fn compound_coroutine_does_not_change_rows() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
for q in [
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u) s ORDER BY 1",
"WITH c AS (SELECT a FROM t INTERSECT SELECT x FROM u) SELECT * FROM c",
"SELECT count(*) FROM (SELECT a FROM t UNION SELECT x FROM u) s",
"SELECT * FROM (SELECT a FROM t EXCEPT SELECT x FROM u) s ORDER BY 1",
] {
let sql = format!("{DATA} {q}");
let a = Command::new("sqlite3")
.arg(":memory:")
.arg(&sql)
.output()
.unwrap();
let b = Command::new(g).arg(":memory:").arg(&sql).output().unwrap();
assert_eq!(
String::from_utf8_lossy(&a.stdout).trim_end(),
String::from_utf8_lossy(&b.stdout).trim_end(),
"rows for {q}"
);
}
}
#[test]
fn compound_body_fragile_shapes_decline() {
let g = env!("CARGO_BIN_EXE_graphitesql");
for q in [
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u)",
"SELECT * FROM (SELECT a FROM t UNION ALL SELECT x FROM u) s",
"SELECT * FROM (SELECT a FROM t UNION SELECT x FROM u) s WHERE a>0",
"WITH c AS (SELECT a FROM t UNION SELECT x FROM u) SELECT * FROM c WHERE a>0",
] {
let got = raw(g, BASE, q);
assert!(
got.contains("EXPLAIN QUERY PLAN for this query shape"),
"{q} should decline as unsupported, got {got:?}"
);
}
}