use serde::{Deserialize, Serialize};
use crate::identifier::{Identifier, QualifiedName};
use crate::ir::constraint::Deferrable;
use crate::ir::default_expr::NormalizedExpr;
use crate::ir::difference::Difference;
use crate::ir::eq::{Equiv, field_difference};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Trigger {
pub qname: QualifiedName,
pub table: QualifiedName,
pub timing: TriggerTiming,
pub events: Vec<TriggerEvent>,
pub level: TriggerLevel,
pub when_clause: Option<NormalizedExpr>,
pub transition_tables: Vec<TransitionTable>,
pub function_qname: QualifiedName,
pub function_args: Vec<String>,
pub is_constraint: bool,
pub deferrable: Deferrable,
pub comment: Option<String>,
}
impl Equiv for Trigger {
fn differences(&self, other: &Self) -> Vec<Difference> {
let Self {
qname: _,
table: _,
timing: _,
events: _,
level: _,
when_clause: _,
transition_tables: _,
function_qname: _,
function_args: _,
is_constraint: _,
deferrable: _,
comment: _,
} = self;
let mut out = Vec::new();
out.extend(field_difference("qname", &self.qname, &other.qname));
out.extend(field_difference("table", &self.table, &other.table));
out.extend(field_difference(
"timing",
&format!("{:?}", self.timing),
&format!("{:?}", other.timing),
));
out.extend(field_difference(
"events",
&format!("{:?}", self.events),
&format!("{:?}", other.events),
));
out.extend(field_difference(
"level",
&format!("{:?}", self.level),
&format!("{:?}", other.level),
));
out.extend(field_difference(
"when_clause",
&format!("{:?}", self.when_clause),
&format!("{:?}", other.when_clause),
));
out.extend(field_difference(
"transition_tables",
&format!("{:?}", self.transition_tables),
&format!("{:?}", other.transition_tables),
));
out.extend(field_difference(
"function_qname",
&self.function_qname,
&other.function_qname,
));
out.extend(field_difference(
"function_args",
&format!("{:?}", self.function_args),
&format!("{:?}", other.function_args),
));
out.extend(field_difference(
"is_constraint",
&self.is_constraint,
&other.is_constraint,
));
out.extend(field_difference(
"deferrable",
&format!("{:?}", self.deferrable),
&format!("{:?}", other.deferrable),
));
out.extend(field_difference(
"comment",
&format!("{:?}", self.comment),
&format!("{:?}", other.comment),
));
out
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TriggerTiming {
Before,
After,
InsteadOf,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum TriggerEvent {
Insert,
Update {
columns: Vec<Identifier>,
},
Delete,
Truncate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TriggerLevel {
Row,
Statement,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TransitionTable {
pub name: Identifier,
pub kind: TransitionKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TransitionKind {
NewTable,
OldTable,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::eq::Equiv;
fn id(s: &str) -> Identifier {
Identifier::from_unquoted(s).unwrap()
}
fn qn(schema: &str, name: &str) -> QualifiedName {
QualifiedName::new(id(schema), id(name))
}
fn trg(name: &str) -> Trigger {
Trigger {
qname: qn("app", name),
table: qn("app", "users"),
timing: TriggerTiming::Before,
events: vec![TriggerEvent::Insert],
level: TriggerLevel::Row,
when_clause: None,
transition_tables: vec![],
function_qname: qn("app", "fn"),
function_args: vec![],
is_constraint: false,
deferrable: Deferrable::NotDeferrable,
comment: None,
}
}
#[test]
fn identical_triggers_diff_empty() {
assert!(trg("t1").canonical_eq(&trg("t1")));
}
#[test]
fn different_timing_diffs_reports_timing() {
let a = trg("t1");
let mut b = trg("t1");
b.timing = TriggerTiming::After;
let d = a.differences(&b);
assert!(d.iter().any(|x| x.path == "timing"));
}
#[test]
fn different_events_diffs() {
let a = trg("t1");
let mut b = trg("t1");
b.events = vec![TriggerEvent::Delete];
let d = a.differences(&b);
assert!(d.iter().any(|x| x.path == "events"));
}
}