#![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();
String::from_utf8_lossy(&o.stdout).into_owned()
}
#[test]
fn without_rowid_upsert_and_returning() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
let cases = [
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3);\
INSERT INTO t VALUES(1,2,9) ON CONFLICT(a,b) DO UPDATE SET c=c+100;\
SELECT quote(a),quote(b),quote(c) FROM t;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3);\
INSERT INTO t VALUES(1,2,9) ON CONFLICT(a,b) DO NOTHING;\
SELECT quote(a),quote(b),quote(c) FROM t;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3);\
INSERT INTO t VALUES(1,2,9) ON CONFLICT DO UPDATE SET c=excluded.c;\
SELECT quote(a),quote(b),quote(c) FROM t;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3);\
INSERT INTO t VALUES(1,2,9) ON CONFLICT(a,b) DO UPDATE SET c=excluded.c WHERE c<0;\
SELECT quote(a),quote(b),quote(c) FROM t;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3);\
INSERT INTO t VALUES(5,6,7) ON CONFLICT(a,b) DO UPDATE SET c=99;\
SELECT quote(a),quote(b),quote(c) FROM t ORDER BY a;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3);\
INSERT INTO t VALUES(1,2,9) ON CONFLICT(a,b) DO UPDATE SET c=100 RETURNING a,b,c;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3) RETURNING c,b,a;",
"CREATE TABLE t(a,b,c UNIQUE,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3),(4,5,6);\
INSERT INTO t VALUES(1,9,6) ON CONFLICT(c) DO UPDATE SET b=99;\
SELECT quote(a),quote(b),quote(c) FROM t ORDER BY a;",
"CREATE TABLE t(a,b,c UNIQUE,PRIMARY KEY(a,b)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2,3),(4,5,6);\
INSERT INTO t VALUES(1,2,9) ON CONFLICT(a,b) DO UPDATE SET c=6;\
SELECT quote(a),quote(b),quote(c) FROM t ORDER BY a;",
];
for sql in cases {
assert_eq!(out("sqlite3", sql), out(g, sql), "for `{sql}`");
}
}
#[test]
fn update_from_reads_all_source_columns() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
let cases = [
"CREATE TABLE t(a,b,c);INSERT INTO t VALUES(9,0,0);\
CREATE TABLE s(k PRIMARY KEY,v);INSERT INTO s VALUES(9,111);\
UPDATE t SET c=s.v FROM s WHERE s.k=t.a;SELECT * FROM t;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a)) WITHOUT ROWID;INSERT INTO t VALUES(9,0,0);\
CREATE TABLE s(k PRIMARY KEY,v);INSERT INTO s VALUES(9,111);\
UPDATE t SET c=s.v FROM s WHERE s.k=t.a;SELECT * FROM t;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a)) WITHOUT ROWID;INSERT INTO t VALUES('q',-1,9);\
CREATE TABLE s(k PRIMARY KEY,v);INSERT INTO s VALUES('q',1);\
UPDATE t SET c=s.v FROM s WHERE s.k=t.a;SELECT * FROM t;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;INSERT INTO t VALUES(1,2,3);\
CREATE TABLE s(k PRIMARY KEY,v);INSERT INTO s VALUES(1,99);\
UPDATE t SET c=v FROM s WHERE s.k=t.a;SELECT * FROM t;",
];
for sql in cases {
assert_eq!(out("sqlite3", sql), out(g, sql), "for `{sql}`");
}
}
#[test]
fn without_rowid_update_constraints() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
let err_cases = [
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;INSERT INTO t VALUES(1,2,3);\
UPDATE t SET b=NULL WHERE a=1;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;INSERT INTO t VALUES(1,2,3);\
UPDATE t SET a=NULL WHERE a=1;",
"CREATE TABLE t(a PRIMARY KEY,c UNIQUE) WITHOUT ROWID;INSERT INTO t VALUES(1,1),(2,2);\
UPDATE t SET c=CASE WHEN c=1 THEN 2 ELSE 1 END;",
];
for sql in err_cases {
let s = out("sqlite3", sql);
let e = Command::new(g).arg(":memory:").arg(sql).output().unwrap();
assert!(s.is_empty(), "sqlite should error for `{sql}`");
let se = String::from_utf8_lossy(&e.stderr);
assert!(
se.contains("constraint failed"),
"graphite should reject `{sql}`, got stderr=`{se}`"
);
}
let ok_cases = [
"CREATE TABLE t(a PRIMARY KEY,c UNIQUE) WITHOUT ROWID;INSERT INTO t VALUES(1,1),(2,2);\
UPDATE t SET c=c+10;SELECT * FROM t ORDER BY a;",
"CREATE TABLE t(a,b,c,PRIMARY KEY(a,b)) WITHOUT ROWID;INSERT INTO t VALUES(1,2,3);\
UPDATE t SET c=NULL WHERE a=1;SELECT * FROM t;",
];
for sql in ok_cases {
assert_eq!(out("sqlite3", sql), out(g, sql), "for `{sql}`");
}
}
#[test]
fn without_rowid_delete_update_returning() {
if !sqlite3_available() {
eprintln!("sqlite3 CLI not found; skipping");
return;
}
let g = env!("CARGO_BIN_EXE_graphitesql");
let cases = [
"CREATE TABLE t(a,b,PRIMARY KEY(a)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2),(3,4);DELETE FROM t WHERE a=1 RETURNING *;",
"CREATE TABLE t(a,b,PRIMARY KEY(a)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2),(3,4);DELETE FROM t RETURNING a;",
"CREATE TABLE t(a,b,PRIMARY KEY(a)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2),(3,4);UPDATE t SET b=b+10 WHERE a=1 RETURNING a,b;",
"CREATE TABLE t(a,b,PRIMARY KEY(a)) WITHOUT ROWID;\
INSERT INTO t VALUES(1,2),(3,4);UPDATE t SET b=b*2 RETURNING b+1;",
];
for sql in cases {
assert_eq!(out("sqlite3", sql), out(g, sql), "for `{sql}`");
}
}