#![cfg(feature = "std")]
use graphitesql::Connection;
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn err(setup: &[&str], stmt: &str) -> String {
let mut c = Connection::open_memory().unwrap();
for s in setup {
if s.trim_start().to_ascii_uppercase().starts_with("PRAGMA") {
let _ = c.execute(s);
} else {
c.execute(s).unwrap();
}
}
let e = if stmt.trim_start().to_ascii_uppercase().starts_with("PRAGMA") {
c.query(stmt).err().map(|e| e.to_string())
} else {
c.execute(stmt).err().map(|e| e.to_string())
};
e.unwrap_or_default()
.trim_start_matches("error: ")
.to_string()
}
#[test]
fn mismatch_from_foreign_key_check() {
let cases: &[(&[&str], &str)] = &[
(
&[
"CREATE TABLE p(id PRIMARY KEY)",
"CREATE TABLE c(pid REFERENCES p(nope))",
],
"PRAGMA foreign_key_check",
),
(
&[
"CREATE TABLE p(id PRIMARY KEY, x)",
"CREATE TABLE c(pid REFERENCES p(x))",
],
"PRAGMA foreign_key_check",
),
(
&[
"CREATE TABLE p(a, b, PRIMARY KEY(a,b))",
"CREATE TABLE c(x REFERENCES p)",
],
"PRAGMA foreign_key_check",
),
(
&["CREATE TABLE p(id)", "CREATE TABLE c(pid REFERENCES p)"],
"PRAGMA foreign_key_check",
),
(
&[
"CREATE TABLE p(a, b, PRIMARY KEY(a,b))",
"CREATE TABLE c(x REFERENCES p(a))",
],
"PRAGMA foreign_key_check",
),
];
for (setup, stmt) in cases {
let e = err(setup, stmt);
assert_eq!(
e, "foreign key mismatch - \"c\" referencing \"p\"",
"{setup:?}"
);
}
let e = err(
&[
"CREATE TABLE p(id PRIMARY KEY)",
"CREATE TABLE c(pid REFERENCES p(nope))",
],
"PRAGMA foreign_key_check(c)",
);
assert_eq!(e, "foreign key mismatch - \"c\" referencing \"p\"");
}
#[test]
fn mismatch_from_write_enforcement() {
let e = err(
&[
"PRAGMA foreign_keys=ON",
"CREATE TABLE p(id PRIMARY KEY)",
"CREATE TABLE c(pid REFERENCES p(nope))",
],
"INSERT INTO c VALUES(1)",
);
assert_eq!(e, "foreign key mismatch - \"c\" referencing \"p\"");
let e = err(
&[
"CREATE TABLE p(id PRIMARY KEY)",
"CREATE TABLE c(pid REFERENCES p(nope))",
"INSERT INTO c VALUES(1)",
"PRAGMA foreign_keys=ON",
],
"UPDATE c SET pid=2",
);
assert_eq!(e, "foreign key mismatch - \"c\" referencing \"p\"");
}
#[test]
fn well_formed_keys_are_not_a_mismatch() {
for setup in [
&[
"PRAGMA foreign_keys=ON",
"CREATE TABLE p(id PRIMARY KEY)",
"CREATE TABLE c(pid REFERENCES p)",
][..],
&[
"PRAGMA foreign_keys=ON",
"CREATE TABLE p(id PRIMARY KEY, x UNIQUE)",
"CREATE TABLE c(pid REFERENCES p(x))",
][..],
&[
"PRAGMA foreign_keys=ON",
"CREATE TABLE p(id PRIMARY KEY, x)",
"CREATE UNIQUE INDEX px ON p(x)",
"CREATE TABLE c(pid REFERENCES p(x))",
][..],
] {
let e = err(setup, "INSERT INTO c VALUES(1)");
assert_eq!(e, "FOREIGN KEY constraint failed", "{setup:?}");
}
let e = err(
&[
"CREATE TABLE p(id PRIMARY KEY)",
"CREATE TABLE c(pid REFERENCES p)",
"INSERT INTO p VALUES(1)",
"INSERT INTO c VALUES(1)",
],
"PRAGMA foreign_key_check",
);
assert_eq!(e, "");
}
#[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);
if !stdout.trim().is_empty() {
return stdout.trim_end().to_string();
}
let 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("error: ")
.to_string();
match (line.rfind(" ("), line.ends_with(')')) {
(Some(i), true)
if line[i + 2..line.len() - 1]
.chars()
.all(|c| c.is_ascii_digit()) =>
{
line[..i].to_string()
}
_ => line,
}
};
for sql in [
"CREATE TABLE p(id PRIMARY KEY); CREATE TABLE c(pid REFERENCES p(nope)); PRAGMA foreign_key_check;",
"CREATE TABLE p(id PRIMARY KEY, x); CREATE TABLE c(pid REFERENCES p(x)); PRAGMA foreign_key_check;",
"CREATE TABLE p(a,b,PRIMARY KEY(a,b)); CREATE TABLE c(x REFERENCES p); PRAGMA foreign_key_check;",
"CREATE TABLE p(id); CREATE TABLE c(pid REFERENCES p); PRAGMA foreign_key_check;",
"CREATE TABLE p(a,b,PRIMARY KEY(a,b)); CREATE TABLE c(x REFERENCES p(a)); PRAGMA foreign_key_check;",
"CREATE TABLE p(id PRIMARY KEY); CREATE TABLE c(pid REFERENCES p(nope)); PRAGMA foreign_key_check(c);",
"PRAGMA foreign_keys=ON; CREATE TABLE p(id PRIMARY KEY); CREATE TABLE c(pid REFERENCES p(nope)); INSERT INTO c VALUES(1);",
"PRAGMA foreign_keys=ON; CREATE TABLE p(id PRIMARY KEY, x); CREATE TABLE c(pid REFERENCES p(x)); INSERT INTO c VALUES(1);",
"PRAGMA foreign_keys=ON; CREATE TABLE p(id PRIMARY KEY); CREATE TABLE c(pid REFERENCES p); INSERT INTO c VALUES(1);",
"PRAGMA foreign_keys=ON; CREATE TABLE p(id PRIMARY KEY, x UNIQUE); CREATE TABLE c(pid REFERENCES p(x)); INSERT INTO c VALUES(1);",
"PRAGMA foreign_keys=ON; CREATE TABLE p(a,b,UNIQUE(a,b)); CREATE TABLE c(x,y,FOREIGN KEY(x,y) REFERENCES p(a,b)); INSERT INTO c VALUES(1,2);",
"CREATE TABLE p(id PRIMARY KEY); CREATE TABLE c(pid REFERENCES p); INSERT INTO p VALUES(1); INSERT INTO c VALUES(1); PRAGMA foreign_key_check;",
] {
assert_eq!(run("sqlite3", sql), run(g, sql), "for {sql}");
}
}