#![cfg(feature = "std")]
use graphitesql::{Connection, Value};
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn err_on(c: &Connection, sql: &str) -> String {
let e = c.query(sql).unwrap_err().to_string();
e.trim_start_matches("SQL error: ")
.trim_start_matches("error: ")
.to_string()
}
fn scalar(sql: &str) -> Value {
let c = Connection::open_memory().unwrap();
c.query(sql).unwrap().rows.into_iter().next().unwrap()[0].clone()
}
#[test]
fn a_nested_aggregate_is_caught_over_an_empty_table() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE t(a)").unwrap();
assert_eq!(
err_on(&c, "SELECT sum(count(*)) FROM t"),
"misuse of aggregate function count()"
);
assert_eq!(
err_on(&c, "SELECT count(sum(a)) FROM t"),
"misuse of aggregate function sum()"
);
assert_eq!(
err_on(&c, "SELECT sum(sum(a)) FROM t"),
"misuse of aggregate function sum()"
);
assert_eq!(
err_on(&c, "SELECT abs(sum(count(*))) FROM t"),
"misuse of aggregate function count()"
);
assert_eq!(
err_on(&c, "SELECT sum(a + count(*)) FROM t"),
"misuse of aggregate function count()"
);
}
#[test]
fn a_nested_window_is_caught_over_an_empty_table() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE t(a)").unwrap();
assert_eq!(
err_on(&c, "SELECT sum(row_number() OVER ()) FROM t"),
"misuse of window function row_number()"
);
assert_eq!(
err_on(&c, "SELECT sum(a + row_number() OVER ()) FROM t"),
"misuse of window function row_number()"
);
}
#[test]
fn the_innermost_call_in_source_order_is_named() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE t(a)").unwrap();
assert_eq!(
err_on(&c, "SELECT sum(count(*) + row_number() OVER ()) FROM t"),
"misuse of window function row_number()"
);
assert_eq!(
err_on(&c, "SELECT sum(row_number() OVER () + count(*)) FROM t"),
"misuse of aggregate function count()"
);
}
#[test]
fn the_check_covers_having_and_order_by() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE t(a)").unwrap();
assert_eq!(
err_on(&c, "SELECT a FROM t GROUP BY a HAVING sum(count(*))"),
"misuse of aggregate function count()"
);
assert_eq!(
err_on(&c, "SELECT sum(a) FROM t ORDER BY sum(count(*))"),
"misuse of aggregate function count()"
);
assert_eq!(
err_on(&c, "SELECT a FROM t ORDER BY sum(row_number() OVER ())"),
"misuse of window function row_number()"
);
}
#[test]
fn legitimate_nesting_is_still_accepted() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE t(a)").unwrap();
c.execute("INSERT INTO t VALUES(1),(2),(3)").unwrap();
assert_eq!(
scalar("SELECT abs(sum(a)) FROM (SELECT 1 a)"),
Value::Integer(1)
);
assert_eq!(
c.query("SELECT max(sum(a), 1) FROM t")
.unwrap()
.rows
.into_iter()
.next()
.unwrap()[0],
Value::Integer(6)
);
assert_eq!(
c.query("SELECT sum(a) OVER () FROM t LIMIT 1")
.unwrap()
.rows
.into_iter()
.next()
.unwrap()[0],
Value::Integer(6)
);
assert_eq!(
c.query("SELECT sum((SELECT count(*) FROM t)) FROM t")
.unwrap()
.rows
.into_iter()
.next()
.unwrap()[0],
Value::Integer(9)
);
}
#[test]
fn matches_sqlite_cli() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
let run = |bin: &str, sql: &str| -> String {
let out = Command::new(bin).arg(":memory:").arg(sql).output().unwrap();
let stdout = String::from_utf8_lossy(&out.stdout);
let line = stdout.lines().next().unwrap_or("").trim_end().to_string();
if !line.is_empty() {
return line;
}
String::from_utf8_lossy(&out.stderr)
.lines()
.find(|l| !l.trim_start().starts_with('^'))
.unwrap_or("")
.trim_start_matches("Error: in prepare, ")
.trim_start_matches("Error: stepping, ")
.trim_start_matches("Error: ")
.trim_start_matches("SQL error: ")
.trim_start_matches("error: ")
.trim_end()
.to_string()
};
for sql in [
"CREATE TABLE t(a); SELECT sum(count(*)) FROM t",
"CREATE TABLE t(a); SELECT count(sum(a)) FROM t",
"CREATE TABLE t(a); SELECT abs(sum(count(*))) FROM t",
"CREATE TABLE t(a); SELECT total(avg(a)) FROM t",
"CREATE TABLE t(a); SELECT sum(row_number() OVER ()) FROM t",
"CREATE TABLE t(a); SELECT sum(a + row_number() OVER ()) FROM t",
"CREATE TABLE t(a); SELECT sum(count(*) + row_number() OVER ()) FROM t",
"CREATE TABLE t(a); SELECT sum(row_number() OVER () + count(*)) FROM t",
"CREATE TABLE t(a); SELECT a FROM t GROUP BY a HAVING sum(count(*))",
"CREATE TABLE t(a); SELECT sum(a) FROM t ORDER BY sum(count(*))",
"CREATE TABLE t(a); SELECT a FROM t ORDER BY sum(row_number() OVER ())",
"CREATE TABLE t(a); INSERT INTO t VALUES(1),(2); SELECT count(sum(a)) FROM t",
"CREATE TABLE t(a); INSERT INTO t VALUES(1),(2); SELECT max(sum(a), 1) FROM t",
"CREATE TABLE t(a); INSERT INTO t VALUES(1),(2); SELECT sum(abs(a)) FROM t",
] {
assert_eq!(run("sqlite3", sql), run(g, sql), "for {sql}");
}
}