1use serde::{Deserialize, Serialize};
4
5use crate::identifier::{Identifier, QualifiedName};
6use crate::ir::constraint::Deferrable;
7use crate::ir::default_expr::NormalizedExpr;
8use crate::ir::eq::DiffMacro;
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, DiffMacro)]
12pub struct Trigger {
13 pub qname: QualifiedName,
15 pub table: QualifiedName,
17 #[diff(via_debug)]
19 pub timing: TriggerTiming,
20 #[diff(via_debug)]
22 pub events: Vec<TriggerEvent>,
23 #[diff(via_debug)]
25 pub level: TriggerLevel,
26 #[diff(via_debug)]
28 pub when_clause: Option<NormalizedExpr>,
29 #[diff(via_debug)]
31 pub transition_tables: Vec<TransitionTable>,
32 pub function_qname: QualifiedName,
34 #[diff(via_debug)]
36 pub function_args: Vec<String>,
37 pub is_constraint: bool,
39 #[diff(via_debug)]
41 pub deferrable: Deferrable,
42 #[diff(via_debug)]
44 pub comment: Option<String>,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
49#[serde(rename_all = "snake_case")]
50pub enum TriggerTiming {
51 Before,
53 After,
55 InsteadOf,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
61#[serde(tag = "kind", rename_all = "snake_case")]
62pub enum TriggerEvent {
63 Insert,
65 Update {
67 columns: Vec<Identifier>,
69 },
70 Delete,
72 Truncate,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
78#[serde(rename_all = "snake_case")]
79pub enum TriggerLevel {
80 Row,
82 Statement,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
88pub struct TransitionTable {
89 pub name: Identifier,
91 pub kind: TransitionKind,
93}
94
95#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
97#[serde(rename_all = "snake_case")]
98pub enum TransitionKind {
99 NewTable,
101 OldTable,
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::ir::eq::Diff;
109
110 fn id(s: &str) -> Identifier {
111 Identifier::from_unquoted(s).unwrap()
112 }
113
114 fn qn(schema: &str, name: &str) -> QualifiedName {
115 QualifiedName::new(id(schema), id(name))
116 }
117
118 fn trg(name: &str) -> Trigger {
119 Trigger {
120 qname: qn("app", name),
121 table: qn("app", "users"),
122 timing: TriggerTiming::Before,
123 events: vec![TriggerEvent::Insert],
124 level: TriggerLevel::Row,
125 when_clause: None,
126 transition_tables: vec![],
127 function_qname: qn("app", "fn"),
128 function_args: vec![],
129 is_constraint: false,
130 deferrable: Deferrable::NotDeferrable,
131 comment: None,
132 }
133 }
134
135 #[test]
136 fn identical_triggers_diff_empty() {
137 assert!(trg("t1").canonical_eq(&trg("t1")));
138 }
139
140 #[test]
141 fn different_timing_diffs_reports_timing() {
142 let a = trg("t1");
143 let mut b = trg("t1");
144 b.timing = TriggerTiming::After;
145 let d = a.diff(&b);
146 assert!(d.iter().any(|x| x.path == "timing"));
147 }
148
149 #[test]
150 fn different_events_diffs() {
151 let a = trg("t1");
152 let mut b = trg("t1");
153 b.events = vec![TriggerEvent::Delete];
154 let d = a.diff(&b);
155 assert!(d.iter().any(|x| x.path == "events"));
156 }
157}