uqa_sql/catalog/events/
rule_condition_binding.rs1use crate::ast::{InternalColumnRef, InternalRelationId, RuleEvent};
10use crate::ir::ScalarExpr;
11use crate::plan::ExpressionPlan;
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct RuleConditionBinding {
16 old_relation: Option<InternalRelationId>,
17 new_relation: Option<InternalRelationId>,
18 columns: Vec<String>,
19}
20
21impl RuleConditionBinding {
22 pub fn for_event(columns: &[String], event: RuleEvent) -> Self {
23 let old_relation = matches!(event, RuleEvent::Update | RuleEvent::Delete)
24 .then(InternalRelationId::allocate);
25 let new_relation = matches!(event, RuleEvent::Insert | RuleEvent::Update)
26 .then(InternalRelationId::allocate);
27 Self {
28 old_relation,
29 new_relation,
30 columns: columns.to_vec(),
31 }
32 }
33
34 pub const fn old_relation(&self) -> Option<InternalRelationId> {
35 self.old_relation
36 }
37
38 pub const fn new_relation(&self) -> Option<InternalRelationId> {
39 self.new_relation
40 }
41
42 pub fn old_column(&self, name: &str) -> Option<InternalColumnRef> {
43 self.column(self.old_relation, name)
44 }
45
46 pub fn new_column(&self, name: &str) -> Option<InternalColumnRef> {
47 self.column(self.new_relation, name)
48 }
49
50 pub fn column_name(&self, column: InternalColumnRef) -> Option<&str> {
51 if Some(column.relation()) != self.old_relation
52 && Some(column.relation()) != self.new_relation
53 {
54 return None;
55 }
56 self.columns.get(column.attribute()).map(String::as_str)
57 }
58
59 pub fn reallocate_plan_relations(&self, plan: &mut ExpressionPlan) -> Self {
61 let rebound = Self {
62 old_relation: self.old_relation.map(|_| InternalRelationId::allocate()),
63 new_relation: self.new_relation.map(|_| InternalRelationId::allocate()),
64 columns: self.columns.clone(),
65 };
66 let mut rewrite = |expression: &mut ScalarExpr| {
67 let ScalarExpr::InternalColumn(column) = expression else {
68 return;
69 };
70 let relation = if Some(column.relation()) == self.old_relation {
71 rebound.old_relation
72 } else if Some(column.relation()) == self.new_relation {
73 rebound.new_relation
74 } else {
75 None
76 };
77 if let Some(relation) = relation {
78 *column = relation.column(column.attribute());
79 }
80 };
81 crate::plan::rewrite_scalar_expression(&mut plan.scalar, &mut rewrite);
82 for subquery in &mut plan.subqueries {
83 subquery.rewrite_scalar_expressions(&mut rewrite);
84 }
85 rebound
86 }
87
88 fn column(
89 &self,
90 relation: Option<InternalRelationId>,
91 name: &str,
92 ) -> Option<InternalColumnRef> {
93 let relation = relation?;
94 self.columns
95 .iter()
96 .position(|column| column == name)
97 .map(|position| relation.column(position))
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn deserialized_plan_relations_are_reallocated_and_rewritten_together() {
107 let binding = RuleConditionBinding {
108 old_relation: Some(InternalRelationId::from_raw(u64::MAX - 1)),
109 new_relation: Some(InternalRelationId::from_raw(u64::MAX)),
110 columns: vec!["id".into()],
111 };
112 let mut plan = ExpressionPlan {
113 scalar: ScalarExpr::InternalColumn(binding.old_column("id").unwrap()),
114 subqueries: Vec::new(),
115 };
116
117 let rebound = binding.reallocate_plan_relations(&mut plan);
118 let ScalarExpr::InternalColumn(column) = plan.scalar else {
119 panic!("condition plan lost its structural OLD column")
120 };
121 assert_eq!(Some(column.relation()), rebound.old_relation());
122 assert_eq!(rebound.column_name(column), Some("id"));
123 assert_ne!(rebound.old_relation(), binding.old_relation());
124 assert_ne!(rebound.new_relation(), binding.new_relation());
125 }
126}