use vibesql_ast::{
CreateTriggerStmt, TriggerAction, TriggerEvent, TriggerGranularity, TriggerTiming,
};
use vibesql_storage::Database;
use vibesql_types::SqlValue;
use crate::{CreateTableExecutor, InsertExecutor, SelectExecutor};
fn exec_sql(db: &mut Database, sql: &str) {
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
match stmt {
vibesql_ast::Statement::CreateTable(s) => {
CreateTableExecutor::execute(&s, db).unwrap();
}
vibesql_ast::Statement::Insert(s) => {
InsertExecutor::execute(db, &s).unwrap();
}
other => panic!("unexpected statement: {:?}", other),
}
}
fn select_rows(db: &Database, sql: &str) -> Vec<Vec<SqlValue>> {
let stmt = vibesql_parser::Parser::parse_sql(sql).unwrap();
match stmt {
vibesql_ast::Statement::Select(s) => {
let executor = SelectExecutor::new(db);
let result = executor.execute_with_columns(&s).unwrap();
result.rows.into_iter().map(|r| r.values.to_vec()).collect()
}
_ => panic!("expected SELECT"),
}
}
#[test]
fn numbered_placeholder_in_trigger_body_fires_as_null() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a INT, b INT);");
exec_sql(&mut db, "CREATE TABLE t2 (c INT, d INT);");
let trigger = CreateTriggerStmt {
if_not_exists: false,
schema: None,
trigger_name: "tr1".to_string(),
name_source: None,
timing: TriggerTiming::After,
event: TriggerEvent::Insert,
table_name: "t1".to_string(),
granularity: TriggerGranularity::Row,
when_condition: None,
triggered_action: TriggerAction::RawSql("INSERT INTO t2 VALUES(?1, ?2);".to_string()),
};
crate::advanced_objects::execute_create_trigger(&trigger, &mut db).unwrap();
exec_sql(&mut db, "INSERT INTO t1 VALUES (1, 2);");
let rows = select_rows(&db, "SELECT c, d FROM t2;");
assert_eq!(rows, vec![vec![SqlValue::Null, SqlValue::Null]]);
}
#[test]
fn anonymous_placeholder_in_trigger_body_fires_as_null() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a INT);");
exec_sql(&mut db, "CREATE TABLE t2 (c INT);");
let trigger = CreateTriggerStmt {
if_not_exists: false,
schema: None,
trigger_name: "tr_anon".to_string(),
name_source: None,
timing: TriggerTiming::After,
event: TriggerEvent::Insert,
table_name: "t1".to_string(),
granularity: TriggerGranularity::Row,
when_condition: None,
triggered_action: TriggerAction::RawSql("INSERT INTO t2 VALUES(?);".to_string()),
};
crate::advanced_objects::execute_create_trigger(&trigger, &mut db).unwrap();
exec_sql(&mut db, "INSERT INTO t1 VALUES (1);");
let rows = select_rows(&db, "SELECT c FROM t2;");
assert_eq!(rows, vec![vec![SqlValue::Null]]);
}
#[test]
fn named_placeholder_in_trigger_body_fires_as_null() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t1 (a INT);");
exec_sql(&mut db, "CREATE TABLE t2 (c INT);");
let trigger = CreateTriggerStmt {
if_not_exists: false,
schema: None,
trigger_name: "tr_named".to_string(),
name_source: None,
timing: TriggerTiming::After,
event: TriggerEvent::Insert,
table_name: "t1".to_string(),
granularity: TriggerGranularity::Row,
when_condition: None,
triggered_action: TriggerAction::RawSql("INSERT INTO t2 VALUES(:p);".to_string()),
};
crate::advanced_objects::execute_create_trigger(&trigger, &mut db).unwrap();
exec_sql(&mut db, "INSERT INTO t1 VALUES (1);");
let rows = select_rows(&db, "SELECT c FROM t2;");
assert_eq!(rows, vec![vec![SqlValue::Null]]);
}
#[test]
fn variable_in_when_clause_and_body_where_fires_as_null() {
let mut db = Database::new();
exec_sql(&mut db, "CREATE TABLE t2 (c, d);");
exec_sql(&mut db, "CREATE TABLE t3 (e, f);");
let when = vibesql_ast::Expression::IsNull {
expr: Box::new(vibesql_ast::Expression::NumberedPlaceholder(1)),
negated: false,
};
let trigger = CreateTriggerStmt {
if_not_exists: false,
schema: None,
trigger_name: "tr2".to_string(),
name_source: None,
timing: TriggerTiming::After,
event: TriggerEvent::Insert,
table_name: "t3".to_string(),
granularity: TriggerGranularity::Row,
when_condition: Some(Box::new(when)),
triggered_action: TriggerAction::RawSql("UPDATE t2 SET c = d WHERE c IS ?2;".to_string()),
};
crate::advanced_objects::execute_create_trigger(&trigger, &mut db).unwrap();
exec_sql(&mut db, "INSERT INTO t2 VALUES('x', 'y');");
exec_sql(&mut db, "INSERT INTO t2 VALUES(NULL, 'z');");
exec_sql(&mut db, "INSERT INTO t3 VALUES(1, 2);");
let t3 = select_rows(&db, "SELECT e, f FROM t3;");
assert_eq!(t3, vec![vec![SqlValue::Integer(1), SqlValue::Integer(2)]]);
let t2 = select_rows(&db, "SELECT c, d FROM t2;");
assert_eq!(
t2,
vec![
vec![
SqlValue::Varchar(arcstr::ArcStr::from("x")),
SqlValue::Varchar(arcstr::ArcStr::from("y")),
],
vec![
SqlValue::Varchar(arcstr::ArcStr::from("z")),
SqlValue::Varchar(arcstr::ArcStr::from("z")),
],
]
);
}