rorm_sql/
create_trigger.rs1use std::fmt::{Display, Formatter};
2
3#[cfg(any(feature = "postgres", feature = "sqlite"))]
4use rorm_declaration::imr::Annotation;
5#[cfg(feature = "sqlite")]
6use rorm_declaration::imr::DbType;
7
8#[cfg(feature = "sqlite")]
9use crate::DBImpl;
10#[cfg(any(feature = "sqlite", feature = "postgres"))]
11use crate::Value;
12
13pub enum SQLCreateTriggerPointInTime {
17 After,
19 Before,
21 InsteadOf,
23}
24
25impl Display for SQLCreateTriggerPointInTime {
26 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
27 match self {
28 SQLCreateTriggerPointInTime::After => write!(f, "AFTER"),
29 SQLCreateTriggerPointInTime::Before => write!(f, "BEFORE"),
30 SQLCreateTriggerPointInTime::InsteadOf => write!(f, "INSTEAD OF"),
31 }
32 }
33}
34
35pub enum SQLCreateTriggerOperation {
39 Delete,
41 Insert,
43 Update {
45 columns: Option<Vec<String>>,
47 },
48}
49
50impl Display for SQLCreateTriggerOperation {
51 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
52 match self {
53 SQLCreateTriggerOperation::Delete => write!(f, "DELETE"),
54 SQLCreateTriggerOperation::Insert => write!(f, "INSERT"),
55 SQLCreateTriggerOperation::Update { columns: None } => write!(f, "UPDATE"),
56 SQLCreateTriggerOperation::Update { columns: Some(c) } => {
57 write!(f, "UPDATE OF {}", c.join(","))
58 }
59 }
60 }
61}
62
63#[cfg(feature = "postgres")]
65pub(crate) fn trigger_annotation_to_trigger_postgres(
66 annotation: &Annotation,
67 table_name: &str,
68 column_name: &str,
69 statements: &mut Vec<(String, Vec<Value>)>,
70) {
71 if annotation == &Annotation::AutoUpdateTime {
72 statements.push(
73 (
74 format!(
75 "CREATE OR REPLACE FUNCTION {table_name}_{column_name}_auto_update_time_update_procedure() RETURNS TRIGGER AS $$ BEGIN NEW.{column_name} = now(); RETURN NEW; END; $$ language 'plpgsql';"
76 ),
77 vec![],
78 )
79 );
80 statements.push((
81 format!(
82 "DROP TRIGGER IF EXISTS {table_name}_{column_name}_auto_update_time_update ON \"{table_name}\";"
83 ),
84 vec![],
85 ));
86 statements.push(
87 (
88 format!(
89 "CREATE TRIGGER {table_name}_{column_name}_auto_update_time_update BEFORE UPDATE ON \"{table_name}\" FOR EACH ROW WHEN (OLD IS DISTINCT FROM NEW) EXECUTE PROCEDURE {table_name}_{column_name}_auto_update_time_update_procedure();"
90 ),
91 vec![],
92 )
93 );
94 }
95}
96
97#[cfg(feature = "sqlite")]
99pub(crate) fn trigger_annotation_to_trigger_sqlite(
100 annotation: &Annotation,
101 db_type: &DbType,
102 table_name: &str,
103 column_name: &str,
104 statements: &mut Vec<(String, Vec<Value>)>,
105) {
106 if annotation == &Annotation::AutoUpdateTime {
107 let update_statement = format!(
108 "UPDATE {} SET {} = {} WHERE ROWID = NEW.ROWID;",
109 table_name,
110 column_name,
111 match db_type {
112 DbType::Date => "CURRENT_DATE",
113 DbType::DateTime => "CURRENT_TIMESTAMP",
114 DbType::Timestamp => "CURRENT_TIMESTAMP",
115 DbType::Time => "CURRENT_TIME",
116 _ => "",
117 }
118 );
119 statements.push((
120 DBImpl::SQLite
121 .create_trigger(
122 format!("{table_name}_{column_name}_auto_update_time").as_str(),
123 table_name,
124 Some(SQLCreateTriggerPointInTime::After),
125 SQLCreateTriggerOperation::Update { columns: None },
126 )
127 .for_each_row()
128 .if_not_exists()
129 .add_statement(update_statement)
130 .build(),
131 vec![],
132 ))
133 }
134}
135
136pub struct SQLCreateTrigger {
140 pub(crate) name: String,
141 pub(crate) table_name: String,
142 pub(crate) if_not_exists: bool,
143 pub(crate) point_in_time: Option<SQLCreateTriggerPointInTime>,
144 pub(crate) operation: SQLCreateTriggerOperation,
145 pub(crate) statements: Vec<String>,
146 pub(crate) for_each_row: bool,
147}
148
149impl SQLCreateTrigger {
150 pub fn if_not_exists(mut self) -> Self {
154 self.if_not_exists = true;
155 self
156 }
157
158 pub fn add_statement(mut self, statement: String) -> Self {
162 self.statements.push(statement);
163 self
164 }
165
166 pub fn for_each_row(mut self) -> Self {
170 self.for_each_row = true;
171 self
172 }
173
174 pub fn build(self) -> String {
178 format!(
179 "CREATE TRIGGER {} {} {} {} ON {}{} BEGIN {} END;",
180 if self.if_not_exists {
181 "IF NOT EXISTS"
182 } else {
183 ""
184 },
185 self.name,
186 match self.point_in_time {
187 None => "".to_string(),
188 Some(s) => s.to_string(),
189 },
190 self.operation,
191 self.table_name,
192 match self.for_each_row {
193 true => " FOR EACH ROW",
194 false => "",
195 },
196 self.statements.join(" "),
197 )
198 }
199}