#![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();
String::from_utf8_lossy(&out.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 rows(bin: &str, base: &str, sql: &str) -> String {
let full = format!("{base} {sql}");
let out = Command::new(bin)
.arg(":memory:")
.arg(&full)
.output()
.unwrap();
String::from_utf8_lossy(&out.stdout).trim_end().to_string()
}
const BASE: &str = "CREATE TABLE t(a INTEGER PRIMARY KEY, b, c, d); CREATE INDEX tb ON t(b); \
CREATE TABLE u(x,y); CREATE INDEX ux ON u(x); CREATE TABLE w(z);";
#[test]
fn in_subquery_list_subquery_matches_sqlite() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
for q in [
"SELECT * FROM t WHERE b NOT IN (SELECT y FROM u)",
"SELECT * FROM t WHERE c NOT IN (SELECT z FROM w)",
"SELECT * FROM t WHERE b NOT IN (SELECT z FROM w WHERE z>3)",
"SELECT * FROM t WHERE b NOT IN (SELECT y FROM u) AND c=5",
"SELECT * FROM t WHERE d IN (SELECT y FROM u)",
"SELECT d FROM t WHERE d IN (SELECT y FROM u)",
"SELECT * FROM t WHERE b IN (SELECT y FROM u)", "SELECT * FROM t WHERE a IN (SELECT x FROM u)", "SELECT * FROM t WHERE rowid IN (SELECT x FROM u)", "SELECT * FROM t WHERE b IN (SELECT y FROM u WHERE x>0)", "SELECT * FROM t WHERE b IN (SELECT y FROM u) AND c=5", ] {
assert_eq!(plan("sqlite3", BASE, q), plan(g, BASE, q), "plan for {q}");
}
}
#[test]
fn in_subquery_out_of_subset_declines_to_bare_scan() {
let g = env!("CARGO_BIN_EXE_graphitesql");
for q in [
"SELECT * FROM t WHERE b IN (SELECT y FROM u WHERE u.y=t.a)", "SELECT * FROM t WHERE b NOT IN (SELECT y FROM u UNION SELECT x FROM u)", "SELECT * FROM t WHERE d IN (SELECT y FROM u) AND (SELECT count(*) FROM w)>0", ] {
assert_eq!(plan(g, BASE, q), "SCAN t", "expected bare SCAN for {q}");
}
}
#[test]
fn in_subquery_rows_unaffected() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
let base = format!(
"{BASE} INSERT INTO t VALUES(1,10,5,7),(2,20,6,8),(3,30,7,7),(4,40,8,9); \
INSERT INTO u VALUES(7,7),(8,8);"
);
for q in [
"SELECT a FROM t WHERE b NOT IN (SELECT y FROM u) ORDER BY a",
"SELECT a FROM t WHERE d IN (SELECT y FROM u) ORDER BY a",
"SELECT count(*) FROM t WHERE c NOT IN (SELECT x FROM u)",
"SELECT a FROM t WHERE b IN (SELECT y FROM u) ORDER BY a", "SELECT a FROM t WHERE a IN (SELECT x FROM u) ORDER BY a", "SELECT a FROM t WHERE b IN (SELECT y FROM u WHERE x<0) ORDER BY a", ] {
assert_eq!(rows("sqlite3", &base, q), rows(g, &base, q), "rows for {q}");
}
}