use std::sync::Arc;
use fallible_iterator::FallibleIterator;
use limbo_sqlite3_parser::ast::{self, Cmd, Stmt, TriggerEvent, TriggerTime};
use limbo_sqlite3_parser::lexer::sql::Parser;
use crate::util::normalize_ident;
use crate::Result;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TriggerOp {
Insert,
Delete,
Update(Option<Vec<String>>),
}
impl TriggerOp {
pub fn matches_update_of(&self, changed_columns: &[String]) -> bool {
match self {
TriggerOp::Update(None) => true,
TriggerOp::Update(Some(cols)) => cols
.iter()
.any(|c| changed_columns.iter().any(|changed| changed == c)),
_ => false,
}
}
}
#[derive(Clone, Debug)]
pub struct Trigger {
pub name: String,
pub tbl_name: String,
pub time: TriggerTime,
pub op: TriggerOp,
pub when_clause: Option<ast::Expr>,
pub commands: Vec<ast::TriggerCmd>,
pub sql: String,
}
impl Trigger {
pub fn from_sql(sql: &str) -> Result<Self> {
let mut parser = Parser::new(sql.as_bytes());
match parser.next()? {
Some(Cmd::Stmt(Stmt::CreateTrigger(create))) => Self::from_ast(&create, sql),
_ => crate::bail_corrupt_error!(
"malformed sqlite_schema entry: expected a CREATE TRIGGER statement, got: {}",
sql
),
}
}
pub fn from_ast(create: &ast::CreateTrigger, sql: &str) -> Result<Self> {
let op = match &create.event {
TriggerEvent::Insert => TriggerOp::Insert,
TriggerEvent::Delete => TriggerOp::Delete,
TriggerEvent::Update => TriggerOp::Update(None),
TriggerEvent::UpdateOf(names) => TriggerOp::Update(Some(
names
.iter()
.map(|n| normalize_ident(n.0.as_str()))
.collect(),
)),
};
Ok(Self {
name: normalize_ident(create.trigger_name.name.0.as_str()),
tbl_name: normalize_ident(create.tbl_name.name.0.as_str()),
time: create.time.unwrap_or(TriggerTime::Before),
op,
when_clause: create.when_clause.clone(),
commands: create.commands.clone(),
sql: sql.to_string(),
})
}
}
impl std::fmt::Display for Trigger {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.sql)
}
}
pub type TriggerRef = Arc<Trigger>;