1use crate::error::{MongrelError, Result};
2use crate::memtable::Value;
3use crate::schema::ColumnDef;
4use serde::de::{self, Deserializer};
5use serde::ser::{SerializeMap, Serializer};
6use serde::{Deserialize, Serialize};
7use sha2::{Digest, Sha256};
8use std::collections::HashSet;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct TriggerEntry {
12 pub trigger: StoredTrigger,
13}
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
16pub struct StoredTrigger {
17 pub name: String,
18 pub version: u64,
19 pub target: TriggerTarget,
20 pub timing: TriggerTiming,
21 pub event: TriggerEvent,
22 #[serde(default)]
23 pub update_of: Vec<String>,
24 #[serde(default)]
25 pub target_columns: Vec<ColumnDef>,
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub when: Option<TriggerExpr>,
28 pub program: TriggerProgram,
29 pub enabled: bool,
30 pub checksum: String,
31 pub created_epoch: u64,
32 pub updated_epoch: u64,
33}
34
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36pub struct TriggerDefinition {
37 pub target: TriggerTarget,
38 pub timing: TriggerTiming,
39 pub event: TriggerEvent,
40 #[serde(default)]
41 pub update_of: Vec<String>,
42 #[serde(default)]
43 pub target_columns: Vec<ColumnDef>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub when: Option<TriggerExpr>,
46 pub program: TriggerProgram,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
50pub struct TriggerConfig {
51 pub recursive_triggers: bool,
52 pub max_depth: u32,
53 #[serde(default)]
54 pub max_loop_iterations: u32,
55}
56
57impl Default for TriggerConfig {
58 fn default() -> Self {
59 Self {
60 recursive_triggers: false,
61 max_depth: 32,
62 max_loop_iterations: 10_000,
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
68#[serde(rename_all = "snake_case", tag = "kind", content = "name")]
69pub enum TriggerTarget {
70 Table(String),
71 View(String),
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
75#[serde(rename_all = "snake_case")]
76pub enum TriggerTiming {
77 Before,
78 After,
79 InsteadOf,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
83#[serde(rename_all = "snake_case")]
84pub enum TriggerEvent {
85 Insert,
86 Update,
87 Delete,
88}
89
90#[derive(Debug, Clone, PartialEq)]
91pub struct TriggerProgram {
92 pub steps: Vec<TriggerStep>,
93}
94
95#[derive(Debug, Clone, PartialEq)]
96pub enum TriggerStep {
97 SetNew {
98 cells: Vec<TriggerCell>,
99 },
100 Insert {
101 table: String,
102 cells: Vec<TriggerCell>,
103 },
104 UpdateByPk {
105 table: String,
106 pk: TriggerValue,
107 cells: Vec<TriggerCell>,
108 },
109 DeleteByPk {
110 table: String,
111 pk: TriggerValue,
112 },
113 Select {
114 id: String,
115 table: String,
116 conditions: Vec<TriggerCondition>,
117 },
118 Foreach {
119 id: String,
120 steps: Vec<TriggerStep>,
121 },
122 DeleteWhere {
123 table: String,
124 conditions: Vec<TriggerCondition>,
125 },
126 UpdateWhere {
127 table: String,
128 conditions: Vec<TriggerCondition>,
129 cells: Vec<TriggerCell>,
130 },
131 Raise {
132 action: TriggerRaiseAction,
133 message: TriggerValue,
134 },
135}
136
137#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
138pub struct TriggerCell {
139 pub column_id: u16,
140 pub value: TriggerValue,
141}
142
143#[derive(Debug, Clone, PartialEq)]
144pub enum TriggerCondition {
145 Pk {
146 value: TriggerValue,
147 },
148 Eq {
149 column_id: u16,
150 value: TriggerValue,
151 },
152 NotEq {
153 column_id: u16,
154 value: TriggerValue,
155 },
156 Lt {
157 column_id: u16,
158 value: TriggerValue,
159 },
160 Lte {
161 column_id: u16,
162 value: TriggerValue,
163 },
164 Gt {
165 column_id: u16,
166 value: TriggerValue,
167 },
168 Gte {
169 column_id: u16,
170 value: TriggerValue,
171 },
172 IsNull {
173 column_id: u16,
174 },
175 IsNotNull {
176 column_id: u16,
177 },
178 And {
179 left: Box<TriggerCondition>,
180 right: Box<TriggerCondition>,
181 },
182 Or {
183 left: Box<TriggerCondition>,
184 right: Box<TriggerCondition>,
185 },
186 Not(Box<TriggerCondition>),
187}
188
189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
190#[serde(rename_all = "snake_case", tag = "kind", content = "value")]
191pub enum TriggerValue {
192 Literal(Value),
193 NewColumn(u16),
194 OldColumn(u16),
195 SelectedColumn(u16),
196}
197
198#[derive(Debug, Clone, PartialEq)]
199pub enum TriggerExpr {
200 Value(TriggerValue),
201 Eq {
202 left: TriggerValue,
203 right: TriggerValue,
204 },
205 NotEq {
206 left: TriggerValue,
207 right: TriggerValue,
208 },
209 Lt {
210 left: TriggerValue,
211 right: TriggerValue,
212 },
213 Lte {
214 left: TriggerValue,
215 right: TriggerValue,
216 },
217 Gt {
218 left: TriggerValue,
219 right: TriggerValue,
220 },
221 Gte {
222 left: TriggerValue,
223 right: TriggerValue,
224 },
225 IsNull(TriggerValue),
226 IsNotNull(TriggerValue),
227 And {
228 left: Box<TriggerExpr>,
229 right: Box<TriggerExpr>,
230 },
231 Or {
232 left: Box<TriggerExpr>,
233 right: Box<TriggerExpr>,
234 },
235 Not(Box<TriggerExpr>),
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
239#[serde(rename_all = "snake_case")]
240pub enum TriggerRaiseAction {
241 Abort,
242 Fail,
243 Rollback,
244 Ignore,
245}
246
247impl Serialize for TriggerProgram {
248 fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
249 let mut map = serializer.serialize_map(Some(1))?;
250 map.serialize_entry("steps", &self.steps)?;
251 map.end()
252 }
253}
254
255impl<'de> Deserialize<'de> for TriggerProgram {
256 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
257 let value = serde_json::Value::deserialize(deserializer)?;
258 let obj = value
259 .as_object()
260 .ok_or_else(|| de::Error::custom("expected object"))?;
261 let steps = match obj.get("steps") {
262 Some(v) => parse_trigger_steps(v).map_err(de::Error::custom)?,
263 None => Vec::new(),
264 };
265 Ok(TriggerProgram { steps })
266 }
267}
268
269impl Serialize for TriggerStep {
270 fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
271 let mut map = serializer.serialize_map(None)?;
272 match self {
273 TriggerStep::SetNew { cells } => {
274 map.serialize_entry("kind", "set_new")?;
275 map.serialize_entry("cells", cells)?;
276 }
277 TriggerStep::Insert { table, cells } => {
278 map.serialize_entry("kind", "insert")?;
279 map.serialize_entry("table", table)?;
280 map.serialize_entry("cells", cells)?;
281 }
282 TriggerStep::UpdateByPk { table, pk, cells } => {
283 map.serialize_entry("kind", "update_by_pk")?;
284 map.serialize_entry("table", table)?;
285 map.serialize_entry("pk", pk)?;
286 map.serialize_entry("cells", cells)?;
287 }
288 TriggerStep::DeleteByPk { table, pk } => {
289 map.serialize_entry("kind", "delete_by_pk")?;
290 map.serialize_entry("table", table)?;
291 map.serialize_entry("pk", pk)?;
292 }
293 TriggerStep::Select {
294 id,
295 table,
296 conditions,
297 } => {
298 map.serialize_entry("kind", "select")?;
299 map.serialize_entry("id", id)?;
300 map.serialize_entry("table", table)?;
301 map.serialize_entry("conditions", conditions)?;
302 }
303 TriggerStep::Foreach { id, steps } => {
304 map.serialize_entry("kind", "foreach")?;
305 map.serialize_entry("id", id)?;
306 map.serialize_entry("steps", steps)?;
307 }
308 TriggerStep::DeleteWhere { table, conditions } => {
309 map.serialize_entry("kind", "delete_where")?;
310 map.serialize_entry("table", table)?;
311 map.serialize_entry("conditions", conditions)?;
312 }
313 TriggerStep::UpdateWhere {
314 table,
315 conditions,
316 cells,
317 } => {
318 map.serialize_entry("kind", "update_where")?;
319 map.serialize_entry("table", table)?;
320 map.serialize_entry("conditions", conditions)?;
321 map.serialize_entry("cells", cells)?;
322 }
323 TriggerStep::Raise { action, message } => {
324 map.serialize_entry("kind", "raise")?;
325 map.serialize_entry("action", action)?;
326 map.serialize_entry("message", message)?;
327 }
328 }
329 map.end()
330 }
331}
332
333impl<'de> Deserialize<'de> for TriggerStep {
334 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
335 let value = serde_json::Value::deserialize(deserializer)?;
336 parse_trigger_step(&value).map_err(de::Error::custom)
337 }
338}
339
340impl Serialize for TriggerCondition {
341 fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
342 let mut map = serializer.serialize_map(None)?;
343 match self {
344 TriggerCondition::Pk { value } => {
345 map.serialize_entry("kind", "pk")?;
346 map.serialize_entry("value", value)?;
347 }
348 TriggerCondition::Eq { column_id, value } => {
349 map.serialize_entry("kind", "eq")?;
350 map.serialize_entry("column_id", column_id)?;
351 map.serialize_entry("value", value)?;
352 }
353 TriggerCondition::NotEq { column_id, value } => {
354 map.serialize_entry("kind", "not_eq")?;
355 map.serialize_entry("column_id", column_id)?;
356 map.serialize_entry("value", value)?;
357 }
358 TriggerCondition::Lt { column_id, value } => {
359 map.serialize_entry("kind", "lt")?;
360 map.serialize_entry("column_id", column_id)?;
361 map.serialize_entry("value", value)?;
362 }
363 TriggerCondition::Lte { column_id, value } => {
364 map.serialize_entry("kind", "lte")?;
365 map.serialize_entry("column_id", column_id)?;
366 map.serialize_entry("value", value)?;
367 }
368 TriggerCondition::Gt { column_id, value } => {
369 map.serialize_entry("kind", "gt")?;
370 map.serialize_entry("column_id", column_id)?;
371 map.serialize_entry("value", value)?;
372 }
373 TriggerCondition::Gte { column_id, value } => {
374 map.serialize_entry("kind", "gte")?;
375 map.serialize_entry("column_id", column_id)?;
376 map.serialize_entry("value", value)?;
377 }
378 TriggerCondition::IsNull { column_id } => {
379 map.serialize_entry("kind", "is_null")?;
380 map.serialize_entry("column_id", column_id)?;
381 }
382 TriggerCondition::IsNotNull { column_id } => {
383 map.serialize_entry("kind", "is_not_null")?;
384 map.serialize_entry("column_id", column_id)?;
385 }
386 TriggerCondition::And { left, right } => {
387 map.serialize_entry("kind", "and")?;
388 map.serialize_entry("left", left)?;
389 map.serialize_entry("right", right)?;
390 }
391 TriggerCondition::Or { left, right } => {
392 map.serialize_entry("kind", "or")?;
393 map.serialize_entry("left", left)?;
394 map.serialize_entry("right", right)?;
395 }
396 TriggerCondition::Not(value) => {
397 map.serialize_entry("kind", "not")?;
398 map.serialize_entry("value", value)?;
399 }
400 }
401 map.end()
402 }
403}
404
405impl<'de> Deserialize<'de> for TriggerCondition {
406 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
407 let value = serde_json::Value::deserialize(deserializer)?;
408 parse_trigger_condition(&value).map_err(de::Error::custom)
409 }
410}
411
412impl Serialize for TriggerExpr {
413 fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
414 let mut map = serializer.serialize_map(None)?;
415 match self {
416 TriggerExpr::Value(value) => {
417 map.serialize_entry("kind", "value")?;
418 map.serialize_entry("value", value)?;
419 }
420 TriggerExpr::Eq { left, right } => {
421 map.serialize_entry("kind", "eq")?;
422 map.serialize_entry("left", left)?;
423 map.serialize_entry("right", right)?;
424 }
425 TriggerExpr::NotEq { left, right } => {
426 map.serialize_entry("kind", "not_eq")?;
427 map.serialize_entry("left", left)?;
428 map.serialize_entry("right", right)?;
429 }
430 TriggerExpr::Lt { left, right } => {
431 map.serialize_entry("kind", "lt")?;
432 map.serialize_entry("left", left)?;
433 map.serialize_entry("right", right)?;
434 }
435 TriggerExpr::Lte { left, right } => {
436 map.serialize_entry("kind", "lte")?;
437 map.serialize_entry("left", left)?;
438 map.serialize_entry("right", right)?;
439 }
440 TriggerExpr::Gt { left, right } => {
441 map.serialize_entry("kind", "gt")?;
442 map.serialize_entry("left", left)?;
443 map.serialize_entry("right", right)?;
444 }
445 TriggerExpr::Gte { left, right } => {
446 map.serialize_entry("kind", "gte")?;
447 map.serialize_entry("left", left)?;
448 map.serialize_entry("right", right)?;
449 }
450 TriggerExpr::IsNull(value) => {
451 map.serialize_entry("kind", "is_null")?;
452 map.serialize_entry("value", value)?;
453 }
454 TriggerExpr::IsNotNull(value) => {
455 map.serialize_entry("kind", "is_not_null")?;
456 map.serialize_entry("value", value)?;
457 }
458 TriggerExpr::And { left, right } => {
459 map.serialize_entry("kind", "and")?;
460 map.serialize_entry("left", left)?;
461 map.serialize_entry("right", right)?;
462 }
463 TriggerExpr::Or { left, right } => {
464 map.serialize_entry("kind", "or")?;
465 map.serialize_entry("left", left)?;
466 map.serialize_entry("right", right)?;
467 }
468 TriggerExpr::Not(value) => {
469 map.serialize_entry("kind", "not")?;
470 map.serialize_entry("value", value)?;
471 }
472 }
473 map.end()
474 }
475}
476
477impl<'de> Deserialize<'de> for TriggerExpr {
478 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
479 let value = serde_json::Value::deserialize(deserializer)?;
480 parse_trigger_expr(&value).map_err(de::Error::custom)
481 }
482}
483
484fn parse_trigger_steps(value: &serde_json::Value) -> std::result::Result<Vec<TriggerStep>, String> {
485 value
486 .as_array()
487 .ok_or_else(|| "expected array for steps".to_string())?
488 .iter()
489 .map(parse_trigger_step)
490 .collect()
491}
492
493fn parse_trigger_step(value: &serde_json::Value) -> std::result::Result<TriggerStep, String> {
494 let obj = value
495 .as_object()
496 .ok_or_else(|| "expected object".to_string())?;
497 let kind = obj
498 .get("kind")
499 .and_then(|v| v.as_str())
500 .ok_or_else(|| "missing kind".to_string())?;
501 match kind {
502 "set_new" => Ok(TriggerStep::SetNew {
503 cells: parse_trigger_cells(
504 obj.get("cells")
505 .ok_or_else(|| "missing cells".to_string())?,
506 )?,
507 }),
508 "insert" => Ok(TriggerStep::Insert {
509 table: parse_string(
510 obj.get("table")
511 .ok_or_else(|| "missing table".to_string())?,
512 "table",
513 )?,
514 cells: parse_trigger_cells(
515 obj.get("cells")
516 .ok_or_else(|| "missing cells".to_string())?,
517 )?,
518 }),
519 "update_by_pk" => Ok(TriggerStep::UpdateByPk {
520 table: parse_string(
521 obj.get("table")
522 .ok_or_else(|| "missing table".to_string())?,
523 "table",
524 )?,
525 pk: parse_trigger_value(obj.get("pk").ok_or_else(|| "missing pk".to_string())?)?,
526 cells: parse_trigger_cells(
527 obj.get("cells")
528 .ok_or_else(|| "missing cells".to_string())?,
529 )?,
530 }),
531 "delete_by_pk" => Ok(TriggerStep::DeleteByPk {
532 table: parse_string(
533 obj.get("table")
534 .ok_or_else(|| "missing table".to_string())?,
535 "table",
536 )?,
537 pk: parse_trigger_value(obj.get("pk").ok_or_else(|| "missing pk".to_string())?)?,
538 }),
539 "select" => Ok(TriggerStep::Select {
540 id: parse_string(obj.get("id").ok_or_else(|| "missing id".to_string())?, "id")?,
541 table: parse_string(
542 obj.get("table")
543 .ok_or_else(|| "missing table".to_string())?,
544 "table",
545 )?,
546 conditions: parse_trigger_conditions_optional(obj)?,
547 }),
548 "foreach" => Ok(TriggerStep::Foreach {
549 id: parse_string(obj.get("id").ok_or_else(|| "missing id".to_string())?, "id")?,
550 steps: parse_trigger_steps(
551 obj.get("steps")
552 .ok_or_else(|| "missing steps".to_string())?,
553 )?,
554 }),
555 "delete_where" => Ok(TriggerStep::DeleteWhere {
556 table: parse_string(
557 obj.get("table")
558 .ok_or_else(|| "missing table".to_string())?,
559 "table",
560 )?,
561 conditions: parse_trigger_conditions_optional(obj)?,
562 }),
563 "update_where" => Ok(TriggerStep::UpdateWhere {
564 table: parse_string(
565 obj.get("table")
566 .ok_or_else(|| "missing table".to_string())?,
567 "table",
568 )?,
569 conditions: parse_trigger_conditions_optional(obj)?,
570 cells: parse_trigger_cells(
571 obj.get("cells")
572 .ok_or_else(|| "missing cells".to_string())?,
573 )?,
574 }),
575 "raise" => Ok(TriggerStep::Raise {
576 action: serde_json::from_value::<TriggerRaiseAction>(
577 obj.get("action")
578 .ok_or_else(|| "missing action".to_string())?
579 .clone(),
580 )
581 .map_err(|e| e.to_string())?,
582 message: parse_trigger_value(
583 obj.get("message")
584 .ok_or_else(|| "missing message".to_string())?,
585 )?,
586 }),
587 _ => Err(format!("unknown TriggerStep kind: {kind}")),
588 }
589}
590
591fn parse_trigger_conditions_optional(
592 obj: &serde_json::Map<String, serde_json::Value>,
593) -> std::result::Result<Vec<TriggerCondition>, String> {
594 match obj.get("conditions") {
595 Some(v) => parse_trigger_conditions(v),
596 None => Ok(Vec::new()),
597 }
598}
599
600fn parse_trigger_conditions(
601 value: &serde_json::Value,
602) -> std::result::Result<Vec<TriggerCondition>, String> {
603 value
604 .as_array()
605 .ok_or_else(|| "expected array for conditions".to_string())?
606 .iter()
607 .map(parse_trigger_condition)
608 .collect()
609}
610
611fn parse_trigger_condition(
612 value: &serde_json::Value,
613) -> std::result::Result<TriggerCondition, String> {
614 let obj = value
615 .as_object()
616 .ok_or_else(|| "expected object".to_string())?;
617 let kind = obj
618 .get("kind")
619 .and_then(|v| v.as_str())
620 .ok_or_else(|| "missing kind".to_string())?;
621 match kind {
622 "pk" => Ok(TriggerCondition::Pk {
623 value: parse_trigger_value(
624 obj.get("value")
625 .ok_or_else(|| "missing value".to_string())?,
626 )?,
627 }),
628 "eq" | "not_eq" | "lt" | "lte" | "gt" | "gte" => parse_trigger_condition_binary(obj, kind),
629 "is_null" => Ok(TriggerCondition::IsNull {
630 column_id: parse_u16(
631 obj.get("column_id")
632 .ok_or_else(|| "missing column_id".to_string())?,
633 "column_id",
634 )?,
635 }),
636 "is_not_null" => Ok(TriggerCondition::IsNotNull {
637 column_id: parse_u16(
638 obj.get("column_id")
639 .ok_or_else(|| "missing column_id".to_string())?,
640 "column_id",
641 )?,
642 }),
643 "and" => Ok(TriggerCondition::And {
644 left: Box::new(parse_trigger_condition(
645 obj.get("left").ok_or_else(|| "missing left".to_string())?,
646 )?),
647 right: Box::new(parse_trigger_condition(
648 obj.get("right")
649 .ok_or_else(|| "missing right".to_string())?,
650 )?),
651 }),
652 "or" => Ok(TriggerCondition::Or {
653 left: Box::new(parse_trigger_condition(
654 obj.get("left").ok_or_else(|| "missing left".to_string())?,
655 )?),
656 right: Box::new(parse_trigger_condition(
657 obj.get("right")
658 .ok_or_else(|| "missing right".to_string())?,
659 )?),
660 }),
661 "not" => Ok(TriggerCondition::Not(Box::new(parse_trigger_condition(
662 obj.get("value")
663 .ok_or_else(|| "missing value".to_string())?,
664 )?))),
665 _ => Err(format!("unknown TriggerCondition kind: {kind}")),
666 }
667}
668
669fn parse_trigger_condition_binary(
670 obj: &serde_json::Map<String, serde_json::Value>,
671 kind: &str,
672) -> std::result::Result<TriggerCondition, String> {
673 let column_id = parse_u16(
674 obj.get("column_id")
675 .ok_or_else(|| "missing column_id".to_string())?,
676 "column_id",
677 )?;
678 let value = parse_trigger_value(
679 obj.get("value")
680 .ok_or_else(|| "missing value".to_string())?,
681 )?;
682 Ok(match kind {
683 "eq" => TriggerCondition::Eq { column_id, value },
684 "not_eq" => TriggerCondition::NotEq { column_id, value },
685 "lt" => TriggerCondition::Lt { column_id, value },
686 "lte" => TriggerCondition::Lte { column_id, value },
687 "gt" => TriggerCondition::Gt { column_id, value },
688 "gte" => TriggerCondition::Gte { column_id, value },
689 _ => return Err(format!("unexpected binary condition kind: {kind}")),
690 })
691}
692
693fn parse_trigger_expr(value: &serde_json::Value) -> std::result::Result<TriggerExpr, String> {
694 let obj = value
695 .as_object()
696 .ok_or_else(|| "expected object".to_string())?;
697 let kind = obj
698 .get("kind")
699 .and_then(|v| v.as_str())
700 .ok_or_else(|| "missing kind".to_string())?;
701 match kind {
702 "value" => Ok(TriggerExpr::Value(parse_trigger_value(
703 obj.get("value")
704 .ok_or_else(|| "missing value".to_string())?,
705 )?)),
706 "eq" | "not_eq" | "lt" | "lte" | "gt" | "gte" => parse_trigger_expr_binary(obj, kind),
707 "is_null" => Ok(TriggerExpr::IsNull(parse_trigger_value(
708 obj.get("value")
709 .ok_or_else(|| "missing value".to_string())?,
710 )?)),
711 "is_not_null" => Ok(TriggerExpr::IsNotNull(parse_trigger_value(
712 obj.get("value")
713 .ok_or_else(|| "missing value".to_string())?,
714 )?)),
715 "and" => Ok(TriggerExpr::And {
716 left: Box::new(parse_trigger_expr(
717 obj.get("left").ok_or_else(|| "missing left".to_string())?,
718 )?),
719 right: Box::new(parse_trigger_expr(
720 obj.get("right")
721 .ok_or_else(|| "missing right".to_string())?,
722 )?),
723 }),
724 "or" => Ok(TriggerExpr::Or {
725 left: Box::new(parse_trigger_expr(
726 obj.get("left").ok_or_else(|| "missing left".to_string())?,
727 )?),
728 right: Box::new(parse_trigger_expr(
729 obj.get("right")
730 .ok_or_else(|| "missing right".to_string())?,
731 )?),
732 }),
733 "not" => Ok(TriggerExpr::Not(Box::new(parse_trigger_expr(
734 obj.get("value")
735 .ok_or_else(|| "missing value".to_string())?,
736 )?))),
737 _ => Err(format!("unknown TriggerExpr kind: {kind}")),
738 }
739}
740
741fn parse_trigger_expr_binary(
742 obj: &serde_json::Map<String, serde_json::Value>,
743 kind: &str,
744) -> std::result::Result<TriggerExpr, String> {
745 let left = parse_trigger_value(obj.get("left").ok_or_else(|| "missing left".to_string())?)?;
746 let right = parse_trigger_value(
747 obj.get("right")
748 .ok_or_else(|| "missing right".to_string())?,
749 )?;
750 Ok(match kind {
751 "eq" => TriggerExpr::Eq { left, right },
752 "not_eq" => TriggerExpr::NotEq { left, right },
753 "lt" => TriggerExpr::Lt { left, right },
754 "lte" => TriggerExpr::Lte { left, right },
755 "gt" => TriggerExpr::Gt { left, right },
756 "gte" => TriggerExpr::Gte { left, right },
757 _ => return Err(format!("unexpected binary expr kind: {kind}")),
758 })
759}
760
761fn parse_trigger_value(value: &serde_json::Value) -> std::result::Result<TriggerValue, String> {
762 serde_json::from_value(value.clone()).map_err(|e| e.to_string())
763}
764
765fn parse_trigger_cells(value: &serde_json::Value) -> std::result::Result<Vec<TriggerCell>, String> {
766 value
767 .as_array()
768 .ok_or_else(|| "expected array for cells".to_string())?
769 .iter()
770 .map(parse_trigger_cell)
771 .collect()
772}
773
774fn parse_trigger_cell(value: &serde_json::Value) -> std::result::Result<TriggerCell, String> {
775 serde_json::from_value(value.clone()).map_err(|e| e.to_string())
776}
777
778fn parse_u16(value: &serde_json::Value, field: &str) -> std::result::Result<u16, String> {
779 value
780 .as_u64()
781 .and_then(|n| u16::try_from(n).ok())
782 .ok_or_else(|| format!("expected u16 for {field}"))
783}
784
785fn parse_string(value: &serde_json::Value, field: &str) -> std::result::Result<String, String> {
786 value
787 .as_str()
788 .map(|s| s.to_string())
789 .ok_or_else(|| format!("expected string for {field}"))
790}
791
792impl StoredTrigger {
793 pub fn new(name: impl Into<String>, definition: TriggerDefinition, epoch: u64) -> Result<Self> {
794 let name = name.into();
795 let checksum = checksum(ChecksumInput {
796 name: &name,
797 version: 1,
798 target: &definition.target,
799 timing: definition.timing,
800 event: definition.event,
801 update_of: &definition.update_of,
802 target_columns: &definition.target_columns,
803 when: &definition.when,
804 program: &definition.program,
805 })?;
806 let trigger = Self {
807 name,
808 version: 1,
809 target: definition.target,
810 timing: definition.timing,
811 event: definition.event,
812 update_of: definition.update_of,
813 target_columns: definition.target_columns,
814 when: definition.when,
815 program: definition.program,
816 enabled: true,
817 checksum,
818 created_epoch: epoch,
819 updated_epoch: epoch,
820 };
821 trigger.validate()?;
822 Ok(trigger)
823 }
824
825 pub fn replaced(&self, mut next: StoredTrigger, epoch: u64) -> Result<StoredTrigger> {
826 next.name = self.name.clone();
827 next.version = self.version + 1;
828 next.created_epoch = self.created_epoch;
829 next.updated_epoch = epoch;
830 next.checksum = checksum(ChecksumInput {
831 name: &next.name,
832 version: next.version,
833 target: &next.target,
834 timing: next.timing,
835 event: next.event,
836 update_of: &next.update_of,
837 target_columns: &next.target_columns,
838 when: &next.when,
839 program: &next.program,
840 })?;
841 next.validate()?;
842 Ok(next)
843 }
844
845 pub fn retarget_table(&self, new_name: impl Into<String>, epoch: u64) -> Result<Self> {
846 let mut next = self.clone();
847 next.version = next.version.saturating_add(1);
848 next.target = TriggerTarget::Table(new_name.into());
849 next.updated_epoch = epoch;
850 next.refresh_checksum()?;
851 Ok(next)
852 }
853
854 pub fn renamed_update_column(
855 &self,
856 old_name: &str,
857 new_name: impl Into<String>,
858 epoch: u64,
859 ) -> Result<Self> {
860 let new_name = new_name.into();
861 let mut next = self.clone();
862 let mut changed = false;
863 for column in &mut next.update_of {
864 if column == old_name {
865 *column = new_name.clone();
866 changed = true;
867 }
868 }
869 if changed {
870 next.version = next.version.saturating_add(1);
871 next.updated_epoch = epoch;
872 next.refresh_checksum()?;
873 }
874 Ok(next)
875 }
876
877 pub fn validate(&self) -> Result<()> {
878 validate_name("trigger", &self.name)?;
879 match &self.target {
880 TriggerTarget::Table(name) => validate_name("trigger table target", name)?,
881 TriggerTarget::View(name) => validate_name("trigger view target", name)?,
882 }
883 match (&self.target, self.timing) {
884 (TriggerTarget::Table(_), TriggerTiming::Before | TriggerTiming::After) => {
885 if !self.target_columns.is_empty() {
886 return Err(MongrelError::InvalidArgument(
887 "table triggers must not carry target_columns".into(),
888 ));
889 }
890 }
891 (TriggerTarget::View(_), TriggerTiming::InsteadOf) => {
892 validate_target_columns(&self.target_columns)?;
893 }
894 (TriggerTarget::Table(_), TriggerTiming::InsteadOf) => {
895 return Err(MongrelError::InvalidArgument(
896 "INSTEAD OF triggers target views, not tables".into(),
897 ));
898 }
899 (TriggerTarget::View(_), TriggerTiming::Before | TriggerTiming::After) => {
900 return Err(MongrelError::InvalidArgument(
901 "views support INSTEAD OF triggers, not BEFORE/AFTER triggers".into(),
902 ));
903 }
904 }
905 let mut update_cols = HashSet::new();
906 for col in &self.update_of {
907 validate_name("trigger UPDATE OF column", col)?;
908 if !update_cols.insert(col) {
909 return Err(MongrelError::InvalidArgument(format!(
910 "duplicate trigger UPDATE OF column {col:?}"
911 )));
912 }
913 }
914 for step in &self.program.steps {
915 step.validate()?;
916 }
917 Ok(())
918 }
919
920 fn refresh_checksum(&mut self) -> Result<()> {
921 self.checksum = checksum(ChecksumInput {
922 name: &self.name,
923 version: self.version,
924 target: &self.target,
925 timing: self.timing,
926 event: self.event,
927 update_of: &self.update_of,
928 target_columns: &self.target_columns,
929 when: &self.when,
930 program: &self.program,
931 })?;
932 self.validate()
933 }
934}
935
936impl TriggerStep {
937 fn validate(&self) -> Result<()> {
938 match self {
939 TriggerStep::SetNew { cells } => {
940 let mut seen = HashSet::new();
941 for cell in cells {
942 if !seen.insert(cell.column_id) {
943 return Err(MongrelError::InvalidArgument(format!(
944 "duplicate trigger cell column id {}",
945 cell.column_id
946 )));
947 }
948 }
949 Ok(())
950 }
951 TriggerStep::Insert { table, cells } | TriggerStep::UpdateByPk { table, cells, .. } => {
952 validate_name("trigger step table", table)?;
953 let mut seen = HashSet::new();
954 for cell in cells {
955 if !seen.insert(cell.column_id) {
956 return Err(MongrelError::InvalidArgument(format!(
957 "duplicate trigger cell column id {}",
958 cell.column_id
959 )));
960 }
961 }
962 Ok(())
963 }
964 TriggerStep::DeleteByPk { table, .. } | TriggerStep::Select { table, .. } => {
965 validate_name("trigger step table", table)
966 }
967 TriggerStep::DeleteWhere { table, .. } => validate_name("trigger step table", table),
968 TriggerStep::UpdateWhere { table, cells, .. } => {
969 validate_name("trigger step table", table)?;
970 let mut seen = HashSet::new();
971 for cell in cells {
972 if !seen.insert(cell.column_id) {
973 return Err(MongrelError::InvalidArgument(format!(
974 "duplicate trigger cell column id {}",
975 cell.column_id
976 )));
977 }
978 }
979 Ok(())
980 }
981 TriggerStep::Foreach { id, .. } => {
982 if id.is_empty() {
983 return Err(MongrelError::InvalidArgument(
984 "foreach id must not be empty".into(),
985 ));
986 }
987 Ok(())
988 }
989 TriggerStep::Raise { .. } => Ok(()),
990 }
991 }
992}
993
994impl From<StoredTrigger> for TriggerEntry {
995 fn from(trigger: StoredTrigger) -> Self {
996 Self { trigger }
997 }
998}
999
1000#[derive(Serialize)]
1001struct ChecksumInput<'a> {
1002 name: &'a str,
1003 version: u64,
1004 target: &'a TriggerTarget,
1005 timing: TriggerTiming,
1006 event: TriggerEvent,
1007 update_of: &'a [String],
1008 target_columns: &'a [ColumnDef],
1009 when: &'a Option<TriggerExpr>,
1010 program: &'a TriggerProgram,
1011}
1012
1013fn checksum(input: ChecksumInput<'_>) -> Result<String> {
1014 let bytes = serde_json::to_vec(&input)
1015 .map_err(|e| MongrelError::Other(format!("trigger checksum serialize: {e}")))?;
1016 Ok(hex(&Sha256::digest(bytes)))
1017}
1018
1019fn hex(bytes: &[u8]) -> String {
1020 const HEX: &[u8; 16] = b"0123456789abcdef";
1021 let mut out = String::with_capacity(bytes.len() * 2);
1022 for b in bytes {
1023 out.push(HEX[(b >> 4) as usize] as char);
1024 out.push(HEX[(b & 0x0f) as usize] as char);
1025 }
1026 out
1027}
1028
1029fn validate_target_columns(columns: &[ColumnDef]) -> Result<()> {
1030 if columns.is_empty() {
1031 return Err(MongrelError::InvalidArgument(
1032 "view triggers require target_columns".into(),
1033 ));
1034 }
1035 let mut names = HashSet::new();
1036 let mut ids = HashSet::new();
1037 for column in columns {
1038 validate_name("trigger target column", &column.name)?;
1039 if !names.insert(&column.name) {
1040 return Err(MongrelError::InvalidArgument(format!(
1041 "duplicate trigger target column {:?}",
1042 column.name
1043 )));
1044 }
1045 if !ids.insert(column.id) {
1046 return Err(MongrelError::InvalidArgument(format!(
1047 "duplicate trigger target column id {}",
1048 column.id
1049 )));
1050 }
1051 }
1052 Ok(())
1053}
1054
1055fn validate_name(kind: &str, name: &str) -> Result<()> {
1056 if name.is_empty()
1057 || !name
1058 .chars()
1059 .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
1060 {
1061 return Err(MongrelError::InvalidArgument(format!(
1062 "{kind} name {name:?} must be non-empty ASCII alphanumeric, '_' or '-'"
1063 )));
1064 }
1065 Ok(())
1066}