1use 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 self.remap_plan(plan, &rebound);
67 rebound
68 }
69
70 pub fn refresh_columns(&self, columns: Vec<String>, plan: &mut ExpressionPlan) -> Self {
72 if self.columns == columns {
73 return self.clone();
74 }
75 let rebound = Self {
76 old_relation: self.old_relation.map(|_| InternalRelationId::allocate()),
77 new_relation: self.new_relation.map(|_| InternalRelationId::allocate()),
78 columns,
79 };
80 self.remap_plan(plan, &rebound);
81 rebound
82 }
83
84 pub fn whole_row_expression(&self, qualifier: &str, table: &str) -> Option<ScalarExpr> {
86 let relation = match qualifier {
87 "old" => self.old_relation?,
88 "new" => self.new_relation?,
89 _ => return None,
90 };
91 Some(ScalarExpr::Cast {
92 implicit: false,
93 expr: Box::new(ScalarExpr::Row(
94 (0..self.columns.len())
95 .map(|position| ScalarExpr::InternalColumn(relation.column(position)))
96 .collect(),
97 )),
98 ty: table.to_string(),
99 })
100 }
101
102 pub fn referenced_columns(
103 &self,
104 plan: &ExpressionPlan,
105 ) -> std::collections::BTreeSet<InternalColumnRef> {
106 let mut required = std::collections::BTreeSet::new();
107 let mut collect = |expression: &ScalarExpr| {
108 expression.visit(&mut |node| {
109 if let ScalarExpr::InternalColumn(column) = node {
110 if self.column_name(*column).is_some() {
111 required.insert(*column);
112 }
113 }
114 });
115 };
116 collect(&plan.scalar);
117 for query in &plan.subqueries {
118 query.visit_scalar_expressions(&mut collect);
119 }
120 required
121 }
122
123 pub fn row_schema(&self, columns: &[crate::ast::ColumnDef]) -> crate::RowSchema {
124 let mut names = Vec::with_capacity(columns.len() * 2);
125 let mut identities = Vec::with_capacity(columns.len() * 2);
126 let mut types = Vec::with_capacity(columns.len() * 2);
127 let mut internal = Vec::with_capacity(columns.len() * 2);
128 for (side, relation) in [("old", self.old_relation()), ("new", self.new_relation())] {
129 let Some(relation) = relation else {
130 continue;
131 };
132 for (attribute, column) in columns.iter().enumerate() {
133 let (name, ty) = (&column.name, &column.ty);
134 let slot = names.len();
135 names.push(name.clone());
136 identities.push(crate::ColumnIdentity::qualified(side, name));
137 types.push(Some(ty.clone()));
138 internal.push((relation.column(attribute), slot, Some(ty.clone())));
139 }
140 }
141 let schema = crate::RowSchema::with_identities(names, identities, types)
142 .with_column_collations(
143 [self.old_relation(), self.new_relation()]
144 .into_iter()
145 .flatten()
146 .flat_map(|_| columns.iter().map(|column| column.collation_oid)),
147 );
148 crate::RowSchema::with_physical_internal_aliases(&schema, &internal)
149 }
150
151 fn remap_plan(&self, plan: &mut ExpressionPlan, rebound: &Self) {
152 let mut rewrite = |expression: &mut ScalarExpr| {
153 let ScalarExpr::InternalColumn(column) = expression else {
154 return;
155 };
156 let relation = if Some(column.relation()) == self.old_relation {
157 rebound.old_relation
158 } else if Some(column.relation()) == self.new_relation {
159 rebound.new_relation
160 } else {
161 None
162 };
163 if let Some(relation) = relation {
164 if let Some(name) = self.column_name(*column) {
165 if let Some(replacement) = rebound.column(Some(relation), name) {
166 *column = replacement;
167 }
168 }
169 }
170 };
171 crate::plan::rewrite_scalar_expression(&mut plan.scalar, &mut rewrite);
172 for subquery in &mut plan.subqueries {
173 subquery.rewrite_scalar_expressions(&mut rewrite);
174 }
175 }
176
177 fn column(
178 &self,
179 relation: Option<InternalRelationId>,
180 name: &str,
181 ) -> Option<InternalColumnRef> {
182 let relation = relation?;
183 self.columns
184 .iter()
185 .position(|column| column == name)
186 .map(|position| relation.column(position))
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn deserialized_plan_relations_are_reallocated_and_rewritten_together() {
196 let binding = RuleConditionBinding {
197 old_relation: Some(InternalRelationId::from_raw(u64::MAX - 1)),
198 new_relation: Some(InternalRelationId::from_raw(u64::MAX)),
199 columns: vec!["id".into()],
200 };
201 let mut plan = ExpressionPlan {
202 scalar: ScalarExpr::InternalColumn(binding.old_column("id").unwrap()),
203 subqueries: Vec::new(),
204 };
205
206 let rebound = binding.reallocate_plan_relations(&mut plan);
207 let ScalarExpr::InternalColumn(column) = plan.scalar else {
208 panic!("condition plan lost its structural OLD column")
209 };
210 assert_eq!(Some(column.relation()), rebound.old_relation());
211 assert_eq!(rebound.column_name(column), Some("id"));
212 assert_ne!(rebound.old_relation(), binding.old_relation());
213 assert_ne!(rebound.new_relation(), binding.new_relation());
214 }
215 #[test]
216 fn refreshed_columns_keep_field_names_and_expand_the_live_whole_row() {
217 let binding = RuleConditionBinding::for_event(
218 &["id".into(), "value".into(), "unused".into()],
219 RuleEvent::Update,
220 );
221 let mut plan = ExpressionPlan {
222 scalar: ScalarExpr::Row(vec![
223 ScalarExpr::InternalColumn(binding.new_column("value").unwrap()),
224 ScalarExpr::Column("old".into()),
225 ]),
226 subqueries: Vec::new(),
227 };
228 let refreshed =
229 binding.refresh_columns(vec!["value".into(), "id".into(), "added".into()], &mut plan);
230 assert_eq!(
231 refreshed.referenced_columns(&plan),
232 std::collections::BTreeSet::from([refreshed.new_column("value").unwrap()])
233 );
234 let ScalarExpr::Row(items) = &plan.scalar else {
235 panic!("outer row");
236 };
237 assert_eq!(
238 items[0],
239 ScalarExpr::InternalColumn(refreshed.new_column("value").unwrap())
240 );
241 let whole = refreshed
242 .whole_row_expression("old", "public.event")
243 .unwrap();
244 let ScalarExpr::Cast { expr, ty, .. } = &whole else {
245 panic!("typed whole row");
246 };
247 assert_eq!(ty, "public.event");
248 assert_eq!(
249 expr.as_ref(),
250 &ScalarExpr::Row(
251 ["value", "id", "added"]
252 .map(|name| ScalarExpr::InternalColumn(refreshed.old_column(name).unwrap()))
253 .to_vec()
254 )
255 );
256 }
257}