use spg_engine::{Engine, QueryResult};
fn err_of(e: &mut Engine, sql: &str) -> String {
match e.execute(sql) {
Err(x) => format!("{x}"),
Ok(_) => panic!("{sql}: expected error, got Ok"),
}
}
fn dump(e: &mut Engine) -> Vec<i32> {
match e.execute("SELECT x FROM t ORDER BY x").unwrap() {
QueryResult::Rows { rows, .. } => rows
.iter()
.map(|r| match &r.values[0] {
spg_storage::Value::Int(n) => *n,
other => panic!("{other:?}"),
})
.collect(),
other => panic!("{other:?}"),
}
}
#[test]
fn nested_view_insert_update_delete() {
let mut e = Engine::new();
e.execute("CREATE TABLE t(x int)").unwrap();
e.execute("CREATE VIEW v1 AS SELECT * FROM t WHERE x > 0")
.unwrap();
e.execute("CREATE VIEW v2 AS SELECT * FROM v1 WHERE x < 100")
.unwrap();
e.execute("INSERT INTO v2 VALUES(60)").unwrap();
assert_eq!(dump(&mut e), vec![60]);
e.execute("UPDATE v2 SET x=70 WHERE x=60").unwrap();
assert_eq!(dump(&mut e), vec![70]);
e.execute("DELETE FROM v2 WHERE x=70").unwrap();
assert_eq!(dump(&mut e), Vec::<i32>::new());
e.execute("INSERT INTO t VALUES(5),(150),(-3),(50)")
.unwrap();
match e.execute("SELECT x FROM v2 ORDER BY x").unwrap() {
QueryResult::Rows { rows, .. } => {
let xs: Vec<i32> = rows
.iter()
.map(|r| match &r.values[0] {
spg_storage::Value::Int(n) => *n,
o => panic!("{o:?}"),
})
.collect();
assert_eq!(xs, vec![5, 50]); }
other => panic!("{other:?}"),
}
}
#[test]
fn cascaded_checks_both_levels_and_names_failing_view() {
let mut e = Engine::new();
e.execute("CREATE TABLE t(x int)").unwrap();
e.execute("CREATE VIEW v1 AS SELECT * FROM t WHERE x > 0")
.unwrap();
e.execute("CREATE VIEW v2c AS SELECT * FROM v1 WHERE x < 100 WITH CASCADED CHECK OPTION")
.unwrap();
let m = err_of(&mut e, "INSERT INTO v2c VALUES(-5)");
assert!(m.contains("view \"v1\""), "{m}");
assert!(m.contains("Failing row contains (-5)"), "{m}");
let m = err_of(&mut e, "INSERT INTO v2c VALUES(200)");
assert!(m.contains("view \"v2c\""), "{m}");
e.execute("INSERT INTO v2c VALUES(80)").unwrap();
assert_eq!(dump(&mut e), vec![80]);
let m = err_of(&mut e, "UPDATE v2c SET x=-1 WHERE x=80");
assert!(m.contains("view \"v1\""), "{m}");
}
#[test]
fn local_does_not_cascade_to_uncheck_underlying() {
let mut e = Engine::new();
e.execute("CREATE TABLE t(x int)").unwrap();
e.execute("CREATE VIEW v1 AS SELECT * FROM t WHERE x > 0")
.unwrap();
e.execute("CREATE VIEW v2l AS SELECT * FROM v1 WHERE x < 100 WITH LOCAL CHECK OPTION")
.unwrap();
e.execute("INSERT INTO v2l VALUES(-9)").unwrap();
assert_eq!(dump(&mut e), vec![-9]);
let m = err_of(&mut e, "INSERT INTO v2l VALUES(500)");
assert!(m.contains("view \"v2l\""), "{m}");
}
#[test]
fn local_cascades_to_check_bearing_underlying() {
let mut e = Engine::new();
e.execute("CREATE TABLE t(x int)").unwrap();
e.execute("CREATE VIEW v1c AS SELECT * FROM t WHERE x > 0 WITH CHECK OPTION")
.unwrap();
e.execute("CREATE VIEW v2ll AS SELECT * FROM v1c WHERE x < 100 WITH LOCAL CHECK OPTION")
.unwrap();
let m = err_of(&mut e, "INSERT INTO v2ll VALUES(-5)");
assert!(m.contains("view \"v1c\""), "{m}");
e.execute("INSERT INTO v2ll VALUES(50)").unwrap();
assert_eq!(dump(&mut e), vec![50]);
}