1use serde::{Deserialize, Serialize};
4
5use crate::identifier::{Identifier, QualifiedName};
6use crate::ir::constraint::Deferrable;
7use crate::ir::default_expr::NormalizedExpr;
8use crate::ir::difference::Difference;
9use crate::ir::eq::{Equiv, field_difference};
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
13pub struct Trigger {
14 pub qname: QualifiedName,
16 pub table: QualifiedName,
18 pub timing: TriggerTiming,
20 pub events: Vec<TriggerEvent>,
22 pub level: TriggerLevel,
24 pub when_clause: Option<NormalizedExpr>,
26 pub transition_tables: Vec<TransitionTable>,
28 pub function_qname: QualifiedName,
30 pub function_args: Vec<String>,
32 pub is_constraint: bool,
34 pub deferrable: Deferrable,
36 pub comment: Option<String>,
38}
39
40impl Equiv for Trigger {
41 fn differences(&self, other: &Self) -> Vec<Difference> {
42 let Self {
43 qname: _,
44 table: _,
45 timing: _,
46 events: _,
47 level: _,
48 when_clause: _,
49 transition_tables: _,
50 function_qname: _,
51 function_args: _,
52 is_constraint: _,
53 deferrable: _,
54 comment: _,
55 } = self;
56 let mut out = Vec::new();
57 out.extend(field_difference("qname", &self.qname, &other.qname));
58 out.extend(field_difference("table", &self.table, &other.table));
59 out.extend(field_difference(
60 "timing",
61 &format!("{:?}", self.timing),
62 &format!("{:?}", other.timing),
63 ));
64 out.extend(field_difference(
65 "events",
66 &format!("{:?}", self.events),
67 &format!("{:?}", other.events),
68 ));
69 out.extend(field_difference(
70 "level",
71 &format!("{:?}", self.level),
72 &format!("{:?}", other.level),
73 ));
74 out.extend(field_difference(
75 "when_clause",
76 &format!("{:?}", self.when_clause),
77 &format!("{:?}", other.when_clause),
78 ));
79 out.extend(field_difference(
80 "transition_tables",
81 &format!("{:?}", self.transition_tables),
82 &format!("{:?}", other.transition_tables),
83 ));
84 out.extend(field_difference(
85 "function_qname",
86 &self.function_qname,
87 &other.function_qname,
88 ));
89 out.extend(field_difference(
90 "function_args",
91 &format!("{:?}", self.function_args),
92 &format!("{:?}", other.function_args),
93 ));
94 out.extend(field_difference(
95 "is_constraint",
96 &self.is_constraint,
97 &other.is_constraint,
98 ));
99 out.extend(field_difference(
100 "deferrable",
101 &format!("{:?}", self.deferrable),
102 &format!("{:?}", other.deferrable),
103 ));
104 out.extend(field_difference(
105 "comment",
106 &format!("{:?}", self.comment),
107 &format!("{:?}", other.comment),
108 ));
109 out
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
115#[serde(rename_all = "snake_case")]
116pub enum TriggerTiming {
117 Before,
119 After,
121 InsteadOf,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
127#[serde(tag = "kind", rename_all = "snake_case")]
128pub enum TriggerEvent {
129 Insert,
131 Update {
133 columns: Vec<Identifier>,
135 },
136 Delete,
138 Truncate,
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
144#[serde(rename_all = "snake_case")]
145pub enum TriggerLevel {
146 Row,
148 Statement,
150}
151
152#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
154pub struct TransitionTable {
155 pub name: Identifier,
157 pub kind: TransitionKind,
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
163#[serde(rename_all = "snake_case")]
164pub enum TransitionKind {
165 NewTable,
167 OldTable,
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use crate::ir::eq::Equiv;
175
176 fn id(s: &str) -> Identifier {
177 Identifier::from_unquoted(s).unwrap()
178 }
179
180 fn qn(schema: &str, name: &str) -> QualifiedName {
181 QualifiedName::new(id(schema), id(name))
182 }
183
184 fn trg(name: &str) -> Trigger {
185 Trigger {
186 qname: qn("app", name),
187 table: qn("app", "users"),
188 timing: TriggerTiming::Before,
189 events: vec![TriggerEvent::Insert],
190 level: TriggerLevel::Row,
191 when_clause: None,
192 transition_tables: vec![],
193 function_qname: qn("app", "fn"),
194 function_args: vec![],
195 is_constraint: false,
196 deferrable: Deferrable::NotDeferrable,
197 comment: None,
198 }
199 }
200
201 #[test]
202 fn identical_triggers_diff_empty() {
203 assert!(trg("t1").canonical_eq(&trg("t1")));
204 }
205
206 #[test]
207 fn different_timing_diffs_reports_timing() {
208 let a = trg("t1");
209 let mut b = trg("t1");
210 b.timing = TriggerTiming::After;
211 let d = a.differences(&b);
212 assert!(d.iter().any(|x| x.path == "timing"));
213 }
214
215 #[test]
216 fn different_events_diffs() {
217 let a = trg("t1");
218 let mut b = trg("t1");
219 b.events = vec![TriggerEvent::Delete];
220 let d = a.differences(&b);
221 assert!(d.iter().any(|x| x.path == "events"));
222 }
223}