#![cfg(feature = "std")]
use graphitesql::Connection;
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn err_msg(sql: &str) -> String {
let mut c = Connection::open_memory().unwrap();
let e = c.execute(sql).unwrap_err().to_string();
e.trim_start_matches("SQL error: ")
.trim_start_matches("error: ")
.to_string()
}
#[test]
fn unknown_table_option_is_named() {
assert_eq!(
err_msg("CREATE TABLE t(a) FOO"),
"unknown table option: FOO"
);
assert_eq!(
err_msg("CREATE TABLE t(a) \"FOO\""),
"unknown table option: \"FOO\""
);
assert_eq!(
err_msg("CREATE TABLE t(a) 'str'"),
"unknown table option: 'str'"
);
assert_eq!(
err_msg("CREATE TABLE t(a) WITHOUT FOO"),
"unknown table option: FOO"
);
}
#[test]
fn first_bad_option_wins_and_suppresses_later_flags() {
assert_eq!(
err_msg("CREATE TABLE t(a) FOO, STRICT"),
"unknown table option: FOO"
);
assert_eq!(
err_msg("CREATE TABLE t(a) FOO, WITHOUT ROWID"),
"unknown table option: FOO"
);
assert_eq!(
err_msg("CREATE TABLE t(a) FOO, BAR"),
"unknown table option: FOO"
);
}
#[test]
fn strict_datatype_check_precedes_a_trailing_bad_option() {
assert_eq!(
err_msg("CREATE TABLE t(a) STRICT, FOO"),
"missing datatype for t.a"
);
assert_eq!(
err_msg("CREATE TABLE t(a INT) STRICT, FOO"),
"unknown table option: FOO"
);
}
#[test]
fn trailing_comma_is_incomplete_input() {
assert_eq!(err_msg("CREATE TABLE t(a) STRICT,"), "incomplete input");
assert_eq!(
err_msg("CREATE TABLE t(a PRIMARY KEY) WITHOUT ROWID,"),
"incomplete input"
);
}
#[test]
fn valid_option_lists_still_parse() {
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE a(x PRIMARY KEY) WITHOUT ROWID")
.unwrap();
c.execute("CREATE TABLE b(x INT) STRICT").unwrap();
c.execute("CREATE TABLE c(x INT PRIMARY KEY) STRICT, WITHOUT ROWID")
.unwrap();
c.execute("CREATE TABLE d(x INT PRIMARY KEY) WITHOUT ROWID, STRICT")
.unwrap();
}
#[test]
fn a_reserved_keyword_in_option_position_is_a_near_syntax_error() {
assert_eq!(
err_msg("CREATE TABLE t(a) AS SELECT 1"),
"near \"AS\": syntax error"
);
assert_eq!(
err_msg("CREATE TABLE t(a, b) AS SELECT 1, 2"),
"near \"AS\": syntax error"
);
assert_eq!(
err_msg("CREATE TABLE t(a) AS VALUES(1)"),
"near \"AS\": syntax error"
);
for kw in ["SELECT", "WHERE", "FROM", "PRIMARY", "ORDER", "JOIN", "NOT"] {
assert_eq!(
err_msg(&format!("CREATE TABLE t(a) {kw}")),
format!("near \"{kw}\": syntax error"),
"{kw}"
);
}
let mut c = Connection::open_memory().unwrap();
c.execute("CREATE TABLE ok AS SELECT 1").unwrap();
assert_eq!(
err_msg("CREATE TABLE t(a) ABORT"),
"unknown table option: ABORT"
);
}
#[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();
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: ")
.trim_start_matches("SQL error: ")
.trim_start_matches("error: ")
.trim_end()
.to_string()
};
for sql in [
"CREATE TABLE t(a) FOO",
"CREATE TABLE t(a) \"FOO\"",
"CREATE TABLE t(a) 'str'",
"CREATE TABLE t(a) WITHOUT FOO",
"CREATE TABLE t(a) FOO, STRICT",
"CREATE TABLE t(a) STRICT, FOO",
"CREATE TABLE t(a INT) STRICT, FOO",
"CREATE TABLE t(a) STRICT,",
"CREATE TABLE t(a) AS SELECT 1",
"CREATE TABLE t(a, b) AS SELECT 1, 2",
"CREATE TABLE t(a) SELECT",
"CREATE TABLE t(a) ABORT",
] {
assert_eq!(run("sqlite3", sql), run(g, sql), "for {sql}");
}
}