#![cfg(feature = "std")]
use graphitesql::{Connection, Value};
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn graphite_err(c: &Connection, sql: &str) -> String {
c.query(sql)
.unwrap_err()
.to_string()
.trim_start_matches("error: ")
.to_string()
}
#[test]
fn row_value_in_scalar_context_is_misused() {
let c = Connection::open_memory().unwrap();
for sql in [
"SELECT (1, 2)",
"SELECT (1, 2, 3)",
"SELECT (1, 2) + 1",
"SELECT 1 WHERE (1, 2)",
"SELECT 1 WHERE (1, 2) = 1",
"SELECT max((1, 2))",
"SELECT abs((1, 2))",
] {
assert_eq!(graphite_err(&c, sql), "row value misused", "for {sql}");
}
assert_eq!(
c.query("SELECT (1, 2) = (1, 2)").unwrap().rows[0][0],
Value::Integer(1)
);
assert_eq!(
c.query("SELECT (1, 2) < (3, 4)").unwrap().rows[0][0],
Value::Integer(1)
);
assert_eq!(
c.query("SELECT (1, 2) IN ((1, 2), (3, 4))").unwrap().rows[0][0],
Value::Integer(1)
);
}
#[test]
fn multicol_subquery_in_comparison() {
let c = Connection::open_memory().unwrap();
for sql in [
"SELECT (SELECT 1, 2) = 1",
"SELECT 1 = (SELECT 1, 2)",
"SELECT (SELECT 1, 2) <= 1",
"SELECT (SELECT 1, 2) <> 1",
"SELECT (SELECT 1, 2) IS 1",
"SELECT (SELECT 1, 2) IS NOT 1",
"SELECT (SELECT 1, 2) = (SELECT 3)",
"SELECT (SELECT 1, 2, 3) = (SELECT 3, 4)",
"SELECT (SELECT 1, 2) BETWEEN 1 AND 2",
"SELECT (SELECT 1, 2) NOT BETWEEN 1 AND 2",
"SELECT 1 BETWEEN (SELECT 1, 2) AND 3",
"SELECT 1 WHERE (SELECT 1, 2) = 1",
"SELECT CASE WHEN (SELECT 1, 2) = 1 THEN 1 END",
"SELECT (VALUES (1, 2)) = 1",
"SELECT (1, 2) = (3, 4, 5)",
] {
assert_eq!(graphite_err(&c, sql), "row value misused", "for {sql}");
}
for (sql, want) in [
("SELECT (SELECT 1, 2) = (SELECT 3, 4)", Value::Integer(0)),
("SELECT (SELECT 1, 2) < (SELECT 3, 4)", Value::Integer(1)),
("SELECT (SELECT 1, 2) = (SELECT 1, 2)", Value::Integer(1)),
("SELECT (SELECT 1, 2) IS (SELECT 3, 4)", Value::Integer(0)),
("SELECT (SELECT 1, 2) IS (SELECT 1, 2)", Value::Integer(1)),
(
"SELECT (SELECT 1, 2) BETWEEN (SELECT 1, 2) AND (SELECT 4, 5)",
Value::Integer(1),
),
(
"SELECT (SELECT 1, 2 WHERE 0) IS (SELECT 3, 4)",
Value::Integer(0),
),
] {
assert_eq!(c.query(sql).unwrap().rows[0][0], want, "for {sql}");
}
}
#[test]
fn matches_sqlite_cli() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let sqlite_err = |sql: &str| -> String {
let out = Command::new("sqlite3")
.arg(":memory:")
.arg(sql)
.output()
.unwrap();
String::from_utf8_lossy(&out.stderr)
.lines()
.next()
.unwrap_or("")
.trim_start_matches("Error: in prepare, ")
.trim_start_matches("Error: stepping, ")
.trim_start_matches("Error: ")
.to_string()
};
let c = Connection::open_memory().unwrap();
for sql in [
"SELECT (1, 2)",
"SELECT (1, 2, 3)",
"SELECT (1, 2) + 1",
"SELECT 1 WHERE (1, 2)",
"SELECT 1 WHERE (1, 2) = 1",
"SELECT max((1, 2))",
"SELECT abs((1, 2))",
"SELECT (SELECT 1, 2) = 1",
"SELECT 1 = (SELECT 1, 2)",
"SELECT (SELECT 1, 2) <> 1",
"SELECT (SELECT 1, 2) IS 1",
"SELECT (SELECT 1, 2) = (SELECT 3)",
"SELECT (SELECT 1, 2, 3) = (SELECT 3, 4)",
"SELECT (SELECT 1, 2) BETWEEN 1 AND 2",
"SELECT 1 BETWEEN (SELECT 1, 2) AND 3",
"SELECT (VALUES (1, 2)) = 1",
"SELECT (1, 2) = (3, 4, 5)",
"SELECT (SELECT 1, 2)",
"SELECT (SELECT 1, 2) IN (1, 2)",
"SELECT (SELECT 1, 2) + 1",
"SELECT (SELECT 1, 2) IS NULL",
] {
assert_eq!(graphite_err(&c, sql), sqlite_err(sql), "for {sql}");
}
let sqlite_val = |sql: &str| -> String {
let out = Command::new("sqlite3")
.arg(":memory:")
.arg(sql)
.output()
.unwrap();
String::from_utf8_lossy(&out.stdout).trim_end().to_string()
};
for sql in [
"SELECT (SELECT 1, 2) = (SELECT 3, 4)",
"SELECT (SELECT 1, 2) < (SELECT 3, 4)",
"SELECT (SELECT 1, 2) IS (SELECT 1, 2)",
"SELECT (VALUES (1, 2)) = (VALUES (3, 4))",
"SELECT (1, 2, 3) BETWEEN (1, 2, 3) AND (4, 5, 6)",
"SELECT (SELECT 1, 2) BETWEEN (SELECT 1, 2) AND (SELECT 4, 5)",
] {
let got = match c.query(sql).unwrap().rows[0][0].clone() {
Value::Integer(i) => i.to_string(),
Value::Null => String::new(),
other => panic!("unexpected {other:?} for {sql}"),
};
assert_eq!(got, sqlite_val(sql), "for {sql}");
}
}