#![cfg(feature = "std")]
use graphitesql::Connection;
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn sqlite_rejects(setup: &[&str], sql: &str) -> bool {
let mut script = String::new();
for s in setup {
script.push_str(s);
script.push_str(";\n");
}
script.push_str(sql);
script.push(';');
let out = Command::new("sqlite3")
.arg(":memory:")
.arg(&script)
.output()
.expect("run sqlite3");
!out.status.success() || !String::from_utf8_lossy(&out.stderr).is_empty()
}
fn setup(c: &mut Connection) {
c.execute("CREATE TABLE t(a, b, c)").unwrap();
c.execute("INSERT INTO t VALUES(1, 2, 3), (4, 5, 6)")
.unwrap();
}
fn both_reject(c: &Connection, setup_sql: &[&str], sql: &str) {
assert!(c.query(sql).is_err(), "graphite should reject: {sql}");
if sqlite3_available() {
assert!(
sqlite_rejects(setup_sql, sql),
"sqlite should reject: {sql}"
);
}
}
#[test]
fn values_rows_must_have_equal_arity() {
let c = Connection::open_memory().unwrap();
both_reject(&c, &[], "VALUES(1, 2), (3)");
both_reject(&c, &[], "VALUES(1), (2, 3)");
both_reject(&c, &[], "VALUES(1, 2, 3), (4, 5), (6, 7, 8)");
both_reject(&c, &[], "SELECT 1, 2 UNION VALUES(1, 2), (3)");
both_reject(&c, &[], "SELECT 1, 2 UNION ALL VALUES(7), (8, 9)");
}
#[test]
fn uniform_values_still_work() {
let c = Connection::open_memory().unwrap();
assert_eq!(
c.query("VALUES(1, 2), (3, 4)").unwrap().rows.len(),
2,
"uniform VALUES must still run"
);
assert_eq!(
c.query("SELECT 1, 2 UNION VALUES(3, 4), (1, 2)")
.unwrap()
.rows
.len(),
2
);
assert_eq!(
c.query("SELECT sum(column1) FROM (VALUES (1), (2), (3))")
.unwrap()
.rows[0][0],
graphitesql::Value::Integer(6)
);
}
#[test]
fn insert_values_arity_mismatch_rejected() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE x(a, b)").unwrap();
assert!(
c.execute("INSERT INTO x VALUES(1, 2), (3)").is_err(),
"INSERT with a short VALUES row must error"
);
if sqlite3_available() {
assert!(sqlite_rejects(
&["CREATE TABLE x(a, b)"],
"INSERT INTO x VALUES(1, 2), (3)"
));
}
c.execute("INSERT INTO x VALUES(1, 2), (3, 4)").unwrap();
assert_eq!(
c.query("SELECT count(*) FROM x").unwrap().rows[0][0],
graphitesql::Value::Integer(2)
);
}
#[test]
fn positional_group_by_out_of_range_rejected() {
let mut c = Connection::open_memory().unwrap();
setup(&mut c);
both_reject(&c, &[], "SELECT 1 GROUP BY 2");
both_reject(&c, &[], "SELECT 1 GROUP BY 0");
both_reject(&c, &[], "SELECT a, b FROM t GROUP BY 3");
both_reject(&c, &[], "SELECT a FROM t GROUP BY -1");
}
#[test]
fn positional_order_by_out_of_range_rejected() {
let mut c = Connection::open_memory().unwrap();
setup(&mut c);
both_reject(&c, &[], "SELECT 1 ORDER BY 2");
both_reject(&c, &[], "SELECT 1 ORDER BY 0");
both_reject(&c, &[], "SELECT 1 ORDER BY -1");
both_reject(&c, &[], "SELECT 1 ORDER BY (2)");
both_reject(&c, &[], "SELECT a, b FROM t ORDER BY 3");
both_reject(&c, &[], "SELECT a FROM t UNION SELECT b FROM t ORDER BY 2");
}
#[test]
fn positional_in_range_still_works() {
let mut c = Connection::open_memory().unwrap();
setup(&mut c);
assert_eq!(
c.query("SELECT a, b, c FROM t ORDER BY 3 DESC")
.unwrap()
.rows[0][0],
graphitesql::Value::Integer(4)
);
assert_eq!(c.query("SELECT * FROM t ORDER BY 3").unwrap().rows.len(), 2);
assert!(c
.query("SELECT a, count(*) FROM t GROUP BY 1 ORDER BY 1")
.is_ok());
assert_eq!(
c.query("SELECT 5 ORDER BY 1 + 1").unwrap().rows[0][0],
graphitesql::Value::Integer(5)
);
}
#[test]
fn compound_column_count_mismatch_rejected() {
let c = Connection::open_memory().unwrap();
both_reject(&c, &[], "SELECT 1 UNION SELECT 1, 2");
both_reject(&c, &[], "SELECT 1, 2 INTERSECT SELECT 1");
both_reject(&c, &[], "SELECT 1 EXCEPT SELECT 1, 2, 3");
both_reject(&c, &[], "SELECT 1 UNION ALL SELECT 1, 2");
}
#[test]
fn matching_compound_widths_still_work() {
let c = Connection::open_memory().unwrap();
assert_eq!(
c.query("SELECT 1, 2 UNION SELECT 3, 4").unwrap().rows.len(),
2
);
assert_eq!(
c.query("SELECT 1 UNION SELECT 1 UNION SELECT 2")
.unwrap()
.rows
.len(),
2
);
}
#[test]
fn aggregate_misuse_still_rejected() {
let mut c = Connection::open_memory().unwrap();
setup(&mut c);
both_reject(
&c,
&["CREATE TABLE t(a, b, c)"],
"SELECT a FROM t WHERE count(*) > 0",
);
both_reject(
&c,
&["CREATE TABLE t(a, b, c)"],
"SELECT count(count(*)) FROM t",
);
both_reject(
&c,
&["CREATE TABLE t(a, b, c)"],
"SELECT a FROM t HAVING a > 1",
);
}