uqa_sql/catalog/events/definition/
rewrites.rs1use super::EventAnalysisContext;
9use crate::{
10 ast::{Expr, FunctionBinding},
11 catalog::{
12 events::{
13 PreparedRuleColumnDrop, RuleCatalog, RuleColumnDependency, StoredRule, TriggerCatalog,
14 },
15 resolution::RelationLookupMode,
16 },
17 plpgsql::{ResolvedVariable, VariableResolver},
18 SQLError,
19};
20use std::collections::BTreeMap;
21use uqa_core::RelationIdentity;
22pub fn rewrite_trigger_routine_references(
23 next_triggers: &mut TriggerCatalog,
24 target: &FunctionBinding,
25 new_name: &str,
26) -> Result<bool, SQLError> {
27 let mut triggers_changed = false;
28 for trigger in next_triggers.values_mut().flat_map(BTreeMap::values_mut) {
29 let invokes_target = match (trigger.function_object_id, target.object_id) {
30 (Some(stored), Some(target)) => stored == target,
31 (None, None) => {
32 target.argument_types.is_empty() && trigger.definition.function == target.name
33 }
34 _ => false,
35 };
36 if invokes_target {
37 trigger.definition.function = new_name.to_string();
38 triggers_changed = true;
39 }
40 if let Some(condition) = &mut trigger.definition.when {
41 triggers_changed |= crate::catalog::stored_ast::rewrite_expression_routine_identity(
42 condition, target, new_name,
43 )?;
44 }
45 }
46
47 Ok(triggers_changed)
48}
49impl EventAnalysisContext<'_> {
50 pub fn rewrite_rule_routine_references(
51 &self,
52 next_rules: &mut RuleCatalog,
53 target: &FunctionBinding,
54 new_name: &str,
55 ) -> Result<bool, SQLError> {
56 let mut rules_changed = false;
57 for (event_relation, entries) in next_rules {
58 for rule in entries.values_mut() {
59 let references_target = rule
60 .dependencies
61 .as_ref()
62 .ok_or_else(|| {
63 SQLError::Internal(format!(
64 "rule `{}` on `{}` has no bound dependency state",
65 rule.definition.name,
66 event_relation.qualified_name()
67 ))
68 })?
69 .routines
70 .iter()
71 .any(
72 |dependency| match (dependency.object_id, target.object_id) {
73 (Some(dependency), Some(target)) => dependency == target,
74 (None, None) => {
75 dependency.name == target.name
76 && dependency.argument_types == target.argument_types
77 }
78 _ => false,
79 },
80 );
81 if !references_target {
82 continue;
83 }
84 if let Some(condition) = &mut rule.definition.condition {
85 crate::catalog::stored_ast::rewrite_expression_routine_identity(
86 condition, target, new_name,
87 )?;
88 }
89 for action in &mut rule.definition.actions {
90 crate::catalog::stored_ast::rewrite_statement_routine_identity(
91 action, target, new_name,
92 )?;
93 }
94 let (validated_relation, condition_plan, condition_binding, dependencies) = self
95 .validate_rule_definition(
96 &mut rule.definition,
97 RelationLookupMode::Bound,
98 None,
99 None,
100 )?;
101 if &validated_relation != event_relation {
102 return Err(SQLError::Internal(format!(
103 "rewritten rule `{}` moved from `{}` to `{}`",
104 rule.definition.name,
105 event_relation.qualified_name(),
106 validated_relation.qualified_name()
107 )));
108 }
109 rule.condition_plan = condition_plan;
110 rule.condition_binding = condition_binding;
111 rule.dependencies = Some(dependencies);
112 rules_changed = true;
113 }
114 }
115
116 Ok(rules_changed)
117 }
118 pub fn renamed_event_column(
119 &self,
120 triggers: &TriggerCatalog,
121 rules: &RuleCatalog,
122 relation: &RelationIdentity,
123 from: &str,
124 to: &str,
125 ) -> Result<Option<(TriggerCatalog, RuleCatalog)>, String> {
126 let dependency = RuleColumnDependency {
127 relation: relation.clone(),
128 column: from.to_string(),
129 };
130 let referenced_by_rule = rules.values().any(|entries| {
131 entries.values().any(|rule| {
132 rule.dependencies
133 .as_ref()
134 .is_some_and(|dependencies| dependencies.columns.contains(&dependency))
135 })
136 });
137 if !triggers.contains_key(relation) && !rules.contains_key(relation) && !referenced_by_rule
138 {
139 return Ok(None);
140 }
141 let mut next_triggers = triggers.clone();
142 let mut next_rules = rules.clone();
143 if let Some(entries) = next_triggers.get_mut(relation) {
144 for trigger in entries.values_mut() {
145 for column in &mut trigger.definition.update_columns {
146 if column == from {
147 *column = to.to_string();
148 }
149 }
150 if let Some(condition) = trigger.definition.when.as_mut() {
151 crate::schema::dependencies::rewrites::rename_schema_expr_column(
152 condition, from, to,
153 )?;
154 }
155 }
156 }
157 self.rewrite_rule_catalog_column(&mut next_rules, &dependency, relation, from, to)?;
158
159 Ok(Some((next_triggers, next_rules)))
160 }
161 pub fn rewrite_rule_catalog_column(
162 &self,
163 rules: &mut BTreeMap<RelationIdentity, BTreeMap<String, StoredRule>>,
164 dependency: &RuleColumnDependency,
165 relation: &RelationIdentity,
166 from: &str,
167 to: &str,
168 ) -> Result<(), String> {
169 for (event_relation, entries) in rules {
170 for rule in entries.values_mut() {
171 let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
172 format!(
173 "rule `{}` on `{}` has no bound dependency state",
174 rule.definition.name,
175 event_relation.qualified_name()
176 )
177 })?;
178 if dependencies.columns.contains(dependency) {
179 self.rewrite_stored_rule_column(rule, event_relation, relation, from, to)?;
180 }
181 }
182 }
183 Ok(())
184 }
185
186 fn rewrite_stored_rule_column(
187 &self,
188 rule: &mut StoredRule,
189 event_relation: &RelationIdentity,
190 relation: &RelationIdentity,
191 from: &str,
192 to: &str,
193 ) -> Result<(), String> {
194 if event_relation == relation {
195 self.rewrite_rule_event_row_column(rule, from, to)?;
196 }
197 crate::binding::stored_columns::rewrite_rule_column_references(
198 self.columns,
199 &mut rule.definition,
200 relation,
201 from,
202 to,
203 )
204 .map_err(|error| {
205 format!(
206 "rewrite rule `{}` column dependency: {error}",
207 rule.definition.name
208 )
209 })?;
210 let (validated_relation, condition_plan, condition_binding, dependencies) = self
211 .validate_rule_definition(&mut rule.definition, RelationLookupMode::Bound, None, None)
212 .map_err(|error| {
213 format!(
214 "rebind rule `{}` after column rename: {error}",
215 rule.definition.name
216 )
217 })?;
218 if validated_relation != *event_relation {
219 return Err(format!(
220 "rule `{}` changed event relation while rebinding column rename",
221 rule.definition.name
222 ));
223 }
224 rule.condition_plan = condition_plan;
225 rule.condition_binding = condition_binding;
226 rule.dependencies = Some(dependencies);
227 Ok(())
228 }
229
230 fn rewrite_rule_event_row_column(
231 &self,
232 rule: &mut StoredRule,
233 from: &str,
234 to: &str,
235 ) -> Result<(), String> {
236 if let Some(condition) = rule.definition.condition.as_mut() {
237 *condition = crate::semantics::rules::action_binding::bind_rule_expr_scoped(
238 condition,
239 &mut RuleColumnResolver {
240 from,
241 to: Some(to),
242 referenced: false,
243 },
244 &std::collections::BTreeSet::new(),
245 )
246 .map_err(|error| format!("rename rule condition column: {error}"))?;
247 }
248 for action in &mut rule.definition.actions {
249 let action_columns = self
250 .rule_action_target_columns(action)
251 .map_err(|error| format!("read rule action columns during rename: {error}"))?;
252 *action = crate::semantics::rules::action_binding::bind_rule_action(
253 self.sources,
254 action,
255 &action_columns,
256 &mut RuleColumnResolver {
257 from,
258 to: Some(to),
259 referenced: false,
260 },
261 )
262 .map_err(|error| format!("rename rule event column: {error}"))?;
263 }
264 Ok(())
265 }
266 pub fn rebind_rule_column_drop(
267 &self,
268 prepared: &mut PreparedRuleColumnDrop,
269 ) -> Result<(), String> {
270 for (event_relation, name) in &prepared.rebind {
271 let rule = prepared
272 .rules
273 .get_mut(event_relation)
274 .and_then(|entries| entries.get_mut(name))
275 .ok_or_else(|| {
276 format!(
277 "rule `{name}` on `{}` disappeared while dropping a column",
278 event_relation.qualified_name()
279 )
280 })?;
281 let (validated_relation, condition_plan, condition_binding, dependencies) = self
282 .validate_rule_definition(
283 &mut rule.definition,
284 RelationLookupMode::Bound,
285 None,
286 None,
287 )
288 .map_err(|error| {
289 format!(
290 "rebind rule `{}` after column drop: {error}",
291 rule.definition.name
292 )
293 })?;
294 if validated_relation != *event_relation {
295 return Err(format!(
296 "rule `{}` changed event relation while rebinding column drop",
297 rule.definition.name
298 ));
299 }
300 rule.condition_plan = condition_plan;
301 rule.condition_binding = condition_binding;
302 rule.dependencies = Some(dependencies);
303 }
304
305 Ok(())
306 }
307}
308
309struct RuleColumnResolver<'a> {
310 from: &'a str,
311 to: Option<&'a str>,
312 referenced: bool,
313}
314
315impl VariableResolver for RuleColumnResolver<'_> {
316 fn resolve_name(&mut self, _name: &str) -> Result<Option<ResolvedVariable>, SQLError> {
317 Ok(None)
318 }
319
320 fn resolve_qualified(
321 &mut self,
322 _qualifier: &str,
323 _column: &str,
324 ) -> Result<Option<ResolvedVariable>, SQLError> {
325 Ok(None)
326 }
327
328 fn resolve_param(&mut self, _index: usize) -> Result<Option<ResolvedVariable>, SQLError> {
329 Ok(None)
330 }
331
332 fn rewrite_qualified(
333 &mut self,
334 qualifier: &str,
335 column: &str,
336 ) -> Result<Option<Expr>, SQLError> {
337 if (qualifier.eq_ignore_ascii_case("old") || qualifier.eq_ignore_ascii_case("new"))
338 && column == self.from
339 {
340 self.referenced = true;
341 if let Some(to) = self.to {
342 return Ok(Some(Expr::qualified_column(qualifier, to)));
343 }
344 }
345 Ok(None)
346 }
347}