use vibesql_ast::Statement;
use vibesql_storage::Database;
use vibesql_types::SqlValue;
fn exec(db: &mut Database, sql: &str) {
let stmt = vibesql_parser::Parser::parse_sql(sql).expect("parse");
match stmt {
Statement::CreateTable(s) => {
crate::CreateTableExecutor::execute(&s, db).unwrap();
}
Statement::CreateTrigger(s) => {
crate::TriggerExecutor::create_trigger(db, &s).unwrap();
}
Statement::Insert(s) => {
crate::InsertExecutor::execute(db, &s).unwrap();
}
Statement::Delete(s) => {
crate::delete::DeleteExecutor::execute(&s, db).unwrap();
}
other => panic!("unsupported statement: {other:?}"),
}
}
fn log_values(db: &Database) -> Vec<String> {
let stmt = vibesql_parser::Parser::parse_sql("SELECT t FROM log").expect("parse select");
let select = match stmt {
Statement::Select(s) => s,
other => panic!("expected SELECT, got {other:?}"),
};
let result =
crate::SelectExecutor::new(db).execute_with_columns(&select).expect("run select");
result
.rows
.iter()
.map(|r| match &r.values[0] {
SqlValue::Varchar(s) => s.to_string(),
other => format!("{other:?}"),
})
.collect()
}
fn setup(db: &mut Database) {
exec(db, "CREATE TABLE t1(a INT PRIMARY KEY, b)");
exec(db, "CREATE TABLE log(t)");
exec(
db,
"CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN \
INSERT INTO log VALUES(old.a || old.b || (SELECT count(*) FROM t1)); END",
);
exec(
db,
"CREATE TRIGGER tr2 BEFORE DELETE ON t1 BEGIN \
INSERT INTO log VALUES(old.a || old.b || (SELECT count(*) FROM t1)); END",
);
exec(db, "INSERT INTO t1 VALUES(1,'one'),(2,'two'),(3,'three')");
}
#[test]
fn before_after_delete_trigger_phase_count_single_row() {
let mut db = Database::new();
setup(&mut db);
exec(&mut db, "DELETE FROM t1 WHERE a=1");
assert_eq!(
log_values(&db),
vec!["1one3".to_string(), "1one2".to_string()],
"BEFORE must see count=3 (row present), AFTER count=2 (row gone)"
);
}
#[test]
fn before_after_delete_trigger_phase_count_multi_row() {
let mut db = Database::new();
setup(&mut db);
exec(&mut db, "DELETE FROM t1 WHERE a=1");
exec(&mut db, "DELETE FROM log");
exec(&mut db, "DELETE FROM t1");
assert_eq!(
log_values(&db),
vec![
"2two2".to_string(),
"2two1".to_string(),
"3three1".to_string(),
"3three0".to_string(),
],
"each phase must observe the live running count"
);
}
#[test]
fn after_only_delete_trigger_phase_count() {
let mut db = Database::new();
exec(&mut db, "CREATE TABLE t1(a INT PRIMARY KEY, b)");
exec(&mut db, "CREATE TABLE log(t)");
exec(
&mut db,
"CREATE TRIGGER tr1 AFTER DELETE ON t1 BEGIN \
INSERT INTO log VALUES(old.a || old.b || (SELECT count(*) FROM t1)); END",
);
exec(&mut db, "INSERT INTO t1 VALUES(1,'one'),(2,'two'),(3,'three')");
exec(&mut db, "DELETE FROM t1 WHERE a=1");
assert_eq!(log_values(&db), vec!["1one2".to_string()], "AFTER sees count=2");
}