Skip to main content

pgevolve_core/ir/
event_trigger.rs

1//! `EVENT TRIGGER` IR. Database-global (bare name, no schema), independently
2//! ownable; modeled like `Publication` (lenient owner, lenient drop in the diff).
3
4use serde::{Deserialize, Serialize};
5
6use crate::identifier::{Identifier, QualifiedName};
7
8/// A `CREATE EVENT TRIGGER` object.
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10pub struct EventTrigger {
11    /// Global object name — event triggers are not schema-qualified.
12    pub name: Identifier,
13    /// The DDL event the trigger fires on.
14    pub event: EventTriggerEvent,
15    /// `WHEN TAG IN (...)` command-tag filter; empty = no filter.
16    /// Canon sorts + dedupes this list.
17    pub tag_filter: Vec<String>,
18    /// Schema-qualified name of the `EXECUTE FUNCTION` function.
19    pub function: QualifiedName,
20    /// Fire state (`pg_event_trigger.evtenabled`).
21    pub enabled: EventTriggerEnabled,
22    /// Lenient owner: `None` = unmanaged (matches `Publication`).
23    pub owner: Option<Identifier>,
24    /// Optional comment.
25    pub comment: Option<String>,
26}
27
28/// The DDL event an event trigger fires on (`pg_event_trigger.evtevent`).
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum EventTriggerEvent {
32    /// `ddl_command_start`
33    DdlCommandStart,
34    /// `ddl_command_end`
35    DdlCommandEnd,
36    /// `sql_drop`
37    SqlDrop,
38    /// `table_rewrite`
39    TableRewrite,
40}
41
42impl EventTriggerEvent {
43    /// The SQL keyword used in `ON <event>`.
44    #[must_use]
45    pub const fn sql(self) -> &'static str {
46        match self {
47            Self::DdlCommandStart => "ddl_command_start",
48            Self::DdlCommandEnd => "ddl_command_end",
49            Self::SqlDrop => "sql_drop",
50            Self::TableRewrite => "table_rewrite",
51        }
52    }
53
54    /// Parse from the SQL event name (lower-cased).
55    #[must_use]
56    pub fn from_sql(s: &str) -> Option<Self> {
57        match s {
58            "ddl_command_start" => Some(Self::DdlCommandStart),
59            "ddl_command_end" => Some(Self::DdlCommandEnd),
60            "sql_drop" => Some(Self::SqlDrop),
61            "table_rewrite" => Some(Self::TableRewrite),
62            _ => None,
63        }
64    }
65}
66
67/// Fire state (`pg_event_trigger.evtenabled`: `O`/`D`/`R`/`A`).
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
69#[serde(rename_all = "snake_case")]
70pub enum EventTriggerEnabled {
71    /// `O` — fires in origin/local sessions (the default).
72    Enabled,
73    /// `D` — never fires.
74    Disabled,
75    /// `R` — fires only when `session_replication_role = replica`.
76    Replica,
77    /// `A` — fires always (origin and replica).
78    Always,
79}
80
81impl EventTriggerEnabled {
82    /// Decode the single-char `pg_event_trigger.evtenabled` code.
83    #[must_use]
84    pub const fn from_pg_char(c: char) -> Option<Self> {
85        match c {
86            'O' => Some(Self::Enabled),
87            'D' => Some(Self::Disabled),
88            'R' => Some(Self::Replica),
89            'A' => Some(Self::Always),
90            _ => None,
91        }
92    }
93
94    /// The `ALTER EVENT TRIGGER name <clause>` body for this state.
95    #[must_use]
96    pub const fn alter_clause(self) -> &'static str {
97        match self {
98            Self::Enabled => "ENABLE",
99            Self::Disabled => "DISABLE",
100            Self::Replica => "ENABLE REPLICA",
101            Self::Always => "ENABLE ALWAYS",
102        }
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn event_sql_roundtrips() {
112        for e in [
113            EventTriggerEvent::DdlCommandStart,
114            EventTriggerEvent::DdlCommandEnd,
115            EventTriggerEvent::SqlDrop,
116            EventTriggerEvent::TableRewrite,
117        ] {
118            assert_eq!(EventTriggerEvent::from_sql(e.sql()), Some(e));
119        }
120        assert_eq!(EventTriggerEvent::from_sql("bogus"), None);
121    }
122
123    #[test]
124    fn enabled_decodes_pg_chars() {
125        assert_eq!(
126            EventTriggerEnabled::from_pg_char('O'),
127            Some(EventTriggerEnabled::Enabled)
128        );
129        assert_eq!(
130            EventTriggerEnabled::from_pg_char('D'),
131            Some(EventTriggerEnabled::Disabled)
132        );
133        assert_eq!(
134            EventTriggerEnabled::from_pg_char('R'),
135            Some(EventTriggerEnabled::Replica)
136        );
137        assert_eq!(
138            EventTriggerEnabled::from_pg_char('A'),
139            Some(EventTriggerEnabled::Always)
140        );
141        assert_eq!(EventTriggerEnabled::from_pg_char('x'), None);
142    }
143
144    #[test]
145    fn alter_clauses() {
146        assert_eq!(EventTriggerEnabled::Disabled.alter_clause(), "DISABLE");
147        assert_eq!(
148            EventTriggerEnabled::Replica.alter_clause(),
149            "ENABLE REPLICA"
150        );
151        assert_eq!(EventTriggerEnabled::Always.alter_clause(), "ENABLE ALWAYS");
152        assert_eq!(EventTriggerEnabled::Enabled.alter_clause(), "ENABLE");
153    }
154}