#![cfg(feature = "std")]
use std::process::Command;
fn sqlite3_available() -> bool {
Command::new("sqlite3").arg("--version").output().is_ok()
}
fn out(bin: &str, sql: &str) -> String {
let o = Command::new(bin).arg(":memory:").arg(sql).output().unwrap();
let mut s = String::from_utf8_lossy(&o.stdout).into_owned();
s.push_str(&String::from_utf8_lossy(&o.stderr));
s.trim_end().to_string()
}
fn norm(s: &str) -> String {
s.lines()
.filter(|l| !l.trim_start().starts_with("CREATE TABLE") && !l.contains("error here"))
.map(|l| {
l.strip_prefix("Error: in prepare, ")
.or_else(|| l.strip_prefix("Error: stepping, "))
.or_else(|| l.strip_prefix("Error: error: "))
.or_else(|| l.strip_prefix("Error: SQL error: "))
.or_else(|| l.strip_prefix("Error: "))
.unwrap_or(l)
.trim_end_matches(|c: char| c.is_ascii_digit())
.trim_end_matches(" (")
.trim_end()
})
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn unparenthesized_default_is_a_literal_term() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
let matching = [
"CREATE TABLE t(b TEXT DEFAULT 'x' NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(b INT DEFAULT 5 NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT -5 NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT -3.5 NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT +5 NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT x'ab' NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT NULL NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT -9223372036854775808 NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT 'x' UNIQUE); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT 'x' COLLATE NOCASE); PRAGMA table_info(t)",
"CREATE TABLE t(b DEFAULT ('x') NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(a, b TEXT DEFAULT 'x' NOT NULL); INSERT INTO t(a) VALUES(1); SELECT * FROM t",
];
for sql in matching {
assert_eq!(out("sqlite3", sql), out(g, sql), "for {sql}");
}
let errors = [
"CREATE TABLE t(b DEFAULT 1+1)",
"CREATE TABLE t(b DEFAULT abs(1))",
"CREATE TABLE t(b DEFAULT 'a' 'b')",
];
for sql in errors {
let s = norm(&out("sqlite3", sql));
let gr = norm(&out(g, sql));
assert!(
s.contains("syntax error"),
"sqlite should reject {sql}: {s}"
);
assert_eq!(s, gr, "error mismatch for {sql}");
}
}
#[test]
fn table_info_reproduces_default_source_text() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
let cases = [
"CREATE TABLE t(a DEFAULT 0x1F); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT -1.5e3 NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT TRUE); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT FALSE NOT NULL); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT CURRENT_TIMESTAMP); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT current_date); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT (1+1)); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT ( 1 + 1 )); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT (( 1 ))); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT (abs(-5))); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT ('x' || 'y')); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT +5); PRAGMA table_info(t)",
"CREATE TABLE t(a DEFAULT 0x1F, b DEFAULT (1+1)); SELECT sql FROM sqlite_master",
];
for sql in cases {
assert_eq!(out("sqlite3", sql), out(g, sql), "for {sql}");
}
}