Skip to main content

dbx_core/automation/
trigger.rs

1//! SQL-based Trigger System
2//!
3//! SQL 표준 호환 Trigger 구현
4
5use serde::{Deserialize, Serialize};
6
7/// SQL Trigger 타이밍
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub enum TriggerTiming {
10    Before,
11    After,
12}
13
14/// SQL Trigger 작업
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16pub enum TriggerOperation {
17    Insert,
18    Update,
19    Delete,
20}
21
22/// FOR EACH 타입
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub enum ForEachType {
25    Row,
26    Statement,
27}
28
29/// SQL 표준 Trigger
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Trigger {
32    /// Trigger 이름
33    pub name: String,
34
35    /// 타이밍 (BEFORE/AFTER)
36    pub timing: TriggerTiming,
37
38    /// 작업 (INSERT/UPDATE/DELETE)
39    pub operation: TriggerOperation,
40
41    /// 대상 테이블
42    pub table: String,
43
44    /// FOR EACH ROW/STATEMENT
45    pub for_each: ForEachType,
46
47    /// WHEN 조건 (SQL 표현식)
48    pub condition: Option<String>,
49
50    /// 실행할 SQL 문장들
51    pub body: Vec<String>,
52
53    /// 생성 시각
54    pub created_at: u64,
55}
56
57impl Trigger {
58    /// 새 SQL Trigger 생성
59    pub fn new(
60        name: impl Into<String>,
61        timing: TriggerTiming,
62        operation: TriggerOperation,
63        table: impl Into<String>,
64        for_each: ForEachType,
65        condition: Option<String>,
66        body: Vec<String>,
67    ) -> Self {
68        Self {
69            name: name.into(),
70            timing,
71            operation,
72            table: table.into(),
73            for_each,
74            condition,
75            body,
76            created_at: std::time::SystemTime::now()
77                .duration_since(std::time::UNIX_EPOCH)
78                .unwrap()
79                .as_secs(),
80        }
81    }
82
83    /// Trigger를 JSON으로 직렬화
84    pub fn to_json(&self) -> crate::error::DbxResult<String> {
85        serde_json::to_string(self).map_err(|e| {
86            crate::error::DbxError::Serialization(format!("Failed to serialize trigger: {}", e))
87        })
88    }
89
90    /// JSON에서 Trigger 역직렬화
91    pub fn from_json(json: &str) -> crate::error::DbxResult<Self> {
92        serde_json::from_str(json).map_err(|e| {
93            crate::error::DbxError::Serialization(format!("Failed to deserialize trigger: {}", e))
94        })
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn test_sql_trigger_creation() {
104        let trigger = Trigger::new(
105            "audit_log",
106            TriggerTiming::After,
107            TriggerOperation::Insert,
108            "users",
109            ForEachType::Row,
110            Some("NEW.age > 18".to_string()),
111            vec!["INSERT INTO audit_logs VALUES (NEW.id, 'INSERT')".to_string()],
112        );
113
114        assert_eq!(trigger.name, "audit_log");
115        assert_eq!(trigger.timing, TriggerTiming::After);
116        assert_eq!(trigger.operation, TriggerOperation::Insert);
117        assert_eq!(trigger.table, "users");
118        assert_eq!(trigger.for_each, ForEachType::Row);
119        assert!(trigger.condition.is_some());
120        assert_eq!(trigger.body.len(), 1);
121    }
122
123    #[test]
124    fn test_sql_trigger_serialization() {
125        let trigger = Trigger::new(
126            "test_trigger",
127            TriggerTiming::Before,
128            TriggerOperation::Update,
129            "products",
130            ForEachType::Row,
131            None,
132            vec!["UPDATE logs SET count = count + 1".to_string()],
133        );
134
135        let json = trigger.to_json().unwrap();
136        let deserialized = Trigger::from_json(&json).unwrap();
137
138        assert_eq!(trigger.name, deserialized.name);
139        assert_eq!(trigger.timing, deserialized.timing);
140        assert_eq!(trigger.operation, deserialized.operation);
141    }
142}