use super::SchemaExpression;
use crate::{Database, DatabaseError};
#[derive(Debug, Clone)]
pub enum TriggerEvent {
Insert,
Update(Vec<String>),
Delete,
}
#[derive(Debug, Clone)]
pub enum TriggerAction {
Abort(String),
Ignore,
}
#[derive(Debug, Clone)]
pub struct CreateTriggerStatement {
pub name: String,
pub table: String,
pub event: Option<TriggerEvent>,
pub predicate: Option<SchemaExpression>,
pub action: Option<TriggerAction>,
pub if_not_exists: bool,
pub temporary: bool,
}
#[must_use]
pub fn create_trigger(name: impl Into<String>, table: impl Into<String>) -> CreateTriggerStatement {
CreateTriggerStatement {
name: name.into(),
table: table.into(),
event: None,
predicate: None,
action: None,
if_not_exists: false,
temporary: false,
}
}
impl CreateTriggerStatement {
#[must_use]
pub const fn temporary(mut self, enabled: bool) -> Self {
self.temporary = enabled;
self
}
#[must_use]
pub fn before_insert(mut self) -> Self {
self.event = Some(TriggerEvent::Insert);
self
}
#[must_use]
pub fn before_update(mut self) -> Self {
self.event = Some(TriggerEvent::Update(Vec::new()));
self
}
#[must_use]
pub fn before_update_of(
mut self,
columns: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
self.event = Some(TriggerEvent::Update(
columns.into_iter().map(Into::into).collect(),
));
self
}
#[must_use]
pub fn before_delete(mut self) -> Self {
self.event = Some(TriggerEvent::Delete);
self
}
#[must_use]
pub fn when(mut self, predicate: SchemaExpression) -> Self {
self.predicate = Some(predicate);
self
}
#[must_use]
pub fn raise_abort(mut self, message: impl Into<String>) -> Self {
self.action = Some(TriggerAction::Abort(message.into()));
self
}
#[must_use]
pub fn raise_ignore(mut self) -> Self {
self.action = Some(TriggerAction::Ignore);
self
}
#[must_use]
pub const fn if_not_exists(mut self, enabled: bool) -> Self {
self.if_not_exists = enabled;
self
}
pub async fn execute(self, db: &dyn Database) -> Result<(), DatabaseError> {
db.exec_create_trigger(&self).await
}
#[cfg(feature = "sqlite-rusqlite")]
pub(crate) fn render_sqlite(&self) -> Result<String, DatabaseError> {
let quote = |name: &str| format!("\"{}\"", name.replace('"', "\"\""));
let event = match &self.event {
Some(TriggerEvent::Insert) => "INSERT".into(),
Some(TriggerEvent::Delete) => "DELETE".into(),
Some(TriggerEvent::Update(columns)) if columns.is_empty() => "UPDATE".into(),
Some(TriggerEvent::Update(columns)) => format!(
"UPDATE OF {}",
columns
.iter()
.map(|name| quote(name))
.collect::<Vec<_>>()
.join(", ")
),
None => return Err(DatabaseError::InvalidSchema("Missing trigger event".into())),
};
let action = match &self.action {
Some(TriggerAction::Abort(message)) => format!(
"RAISE(ABORT, {})",
SchemaExpression::Text(message.clone()).render('"')?
),
Some(TriggerAction::Ignore) => "RAISE(IGNORE)".into(),
None => {
return Err(DatabaseError::InvalidSchema(
"Missing trigger action".into(),
));
}
};
let predicate = self
.predicate
.as_ref()
.map(|predicate| predicate.render('"').map(|sql| format!(" WHEN {sql}")))
.transpose()?
.unwrap_or_default();
Ok(format!(
"CREATE {}TRIGGER {}{} BEFORE {event} ON {}{predicate} BEGIN SELECT {action}; END",
if self.temporary { "TEMP " } else { "" },
if self.if_not_exists {
"IF NOT EXISTS "
} else {
""
},
quote(&self.name),
quote(&self.table)
))
}
}