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 crate::catalog::events::synchronize_rule_sql_text(&mut rule.definition)?;
95 let (validated_relation, condition_plan, condition_binding, dependencies) = self
96 .validate_rule_definition(
97 &mut rule.definition,
98 RelationLookupMode::Bound,
99 None,
100 None,
101 )?;
102 if &validated_relation != event_relation {
103 return Err(SQLError::Internal(format!(
104 "rewritten rule `{}` moved from `{}` to `{}`",
105 rule.definition.name,
106 event_relation.qualified_name(),
107 validated_relation.qualified_name()
108 )));
109 }
110 rule.condition_plan = condition_plan;
111 rule.condition_binding = condition_binding;
112 rule.dependencies = Some(dependencies);
113 rules_changed = true;
114 }
115 }
116
117 Ok(rules_changed)
118 }
119 pub fn renamed_event_column(
120 &self,
121 triggers: &TriggerCatalog,
122 rules: &RuleCatalog,
123 relation: &RelationIdentity,
124 from: &str,
125 to: &str,
126 ) -> Result<Option<(TriggerCatalog, RuleCatalog)>, String> {
127 let dependency = RuleColumnDependency {
128 relation: relation.clone(),
129 column: from.to_string(),
130 };
131 let referenced_by_rule = rules.values().any(|entries| {
132 entries.values().any(|rule| {
133 rule.dependencies
134 .as_ref()
135 .is_some_and(|dependencies| dependencies.columns.contains(&dependency))
136 })
137 });
138 if !triggers.contains_key(relation) && !rules.contains_key(relation) && !referenced_by_rule
139 {
140 return Ok(None);
141 }
142 let mut next_triggers = triggers.clone();
143 let mut next_rules = rules.clone();
144 if let Some(entries) = next_triggers.get_mut(relation) {
145 for trigger in entries.values_mut() {
146 for column in &mut trigger.definition.update_columns {
147 if column == from {
148 *column = to.to_string();
149 }
150 }
151 if let Some(condition) = trigger.definition.when.as_mut() {
152 crate::schema::dependencies::rewrites::rename_schema_expr_column(
153 condition, from, to,
154 )?;
155 }
156 }
157 }
158 self.rewrite_rule_catalog_column(&mut next_rules, &dependency, relation, from, to)?;
159
160 Ok(Some((next_triggers, next_rules)))
161 }
162 pub fn rewrite_rule_catalog_column(
163 &self,
164 rules: &mut BTreeMap<RelationIdentity, BTreeMap<String, StoredRule>>,
165 dependency: &RuleColumnDependency,
166 relation: &RelationIdentity,
167 from: &str,
168 to: &str,
169 ) -> Result<(), String> {
170 for (event_relation, entries) in rules {
171 for rule in entries.values_mut() {
172 let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
173 format!(
174 "rule `{}` on `{}` has no bound dependency state",
175 rule.definition.name,
176 event_relation.qualified_name()
177 )
178 })?;
179 if dependencies.columns.contains(dependency) {
180 self.rewrite_stored_rule_column(rule, event_relation, relation, from, to)?;
181 }
182 }
183 }
184 Ok(())
185 }
186
187 fn rewrite_stored_rule_column(
188 &self,
189 rule: &mut StoredRule,
190 event_relation: &RelationIdentity,
191 relation: &RelationIdentity,
192 from: &str,
193 to: &str,
194 ) -> Result<(), String> {
195 if event_relation == relation {
196 self.rewrite_rule_event_row_column(rule, from, to)?;
197 }
198 crate::binding::stored_columns::rewrite_rule_column_references(
199 self.columns,
200 &mut rule.definition,
201 relation,
202 from,
203 to,
204 )
205 .map_err(|error| {
206 format!(
207 "rewrite rule `{}` column dependency: {error}",
208 rule.definition.name
209 )
210 })?;
211 let (validated_relation, condition_plan, condition_binding, dependencies) = self
212 .validate_rule_definition(&mut rule.definition, RelationLookupMode::Bound, None, None)
213 .map_err(|error| {
214 format!(
215 "rebind rule `{}` after column rename: {error}",
216 rule.definition.name
217 )
218 })?;
219 if validated_relation != *event_relation {
220 return Err(format!(
221 "rule `{}` changed event relation while rebinding column rename",
222 rule.definition.name
223 ));
224 }
225 rule.condition_plan = condition_plan;
226 rule.condition_binding = condition_binding;
227 rule.dependencies = Some(dependencies);
228 Ok(())
229 }
230
231 fn rewrite_rule_event_row_column(
232 &self,
233 rule: &mut StoredRule,
234 from: &str,
235 to: &str,
236 ) -> Result<(), String> {
237 if let Some(condition) = rule.definition.condition.as_mut() {
238 *condition = crate::semantics::rules::action_binding::bind_rule_expr_scoped(
239 condition,
240 &mut RuleColumnResolver {
241 from,
242 to: Some(to),
243 referenced: false,
244 },
245 &std::collections::BTreeSet::new(),
246 )
247 .map_err(|error| format!("rename rule condition column: {error}"))?;
248 }
249 for action in &mut rule.definition.actions {
250 let action_columns = self
251 .rule_action_target_columns(action)
252 .map_err(|error| format!("read rule action columns during rename: {error}"))?;
253 *action = crate::semantics::rules::action_binding::bind_rule_action(
254 self.sources,
255 action,
256 &action_columns,
257 &mut RuleColumnResolver {
258 from,
259 to: Some(to),
260 referenced: false,
261 },
262 )
263 .map_err(|error| format!("rename rule event column: {error}"))?;
264 }
265 Ok(())
266 }
267 pub fn rebind_rule_column_drop(
268 &self,
269 prepared: &mut PreparedRuleColumnDrop,
270 ) -> Result<(), String> {
271 for (event_relation, name) in &prepared.rebind {
272 let rule = prepared
273 .rules
274 .get_mut(event_relation)
275 .and_then(|entries| entries.get_mut(name))
276 .ok_or_else(|| {
277 format!(
278 "rule `{name}` on `{}` disappeared while dropping a column",
279 event_relation.qualified_name()
280 )
281 })?;
282 let (validated_relation, condition_plan, condition_binding, dependencies) = self
283 .validate_rule_definition(
284 &mut rule.definition,
285 RelationLookupMode::Bound,
286 None,
287 None,
288 )
289 .map_err(|error| {
290 format!(
291 "rebind rule `{}` after column drop: {error}",
292 rule.definition.name
293 )
294 })?;
295 if validated_relation != *event_relation {
296 return Err(format!(
297 "rule `{}` changed event relation while rebinding column drop",
298 rule.definition.name
299 ));
300 }
301 rule.condition_plan = condition_plan;
302 rule.condition_binding = condition_binding;
303 rule.dependencies = Some(dependencies);
304 }
305
306 Ok(())
307 }
308}
309
310struct RuleColumnResolver<'a> {
311 from: &'a str,
312 to: Option<&'a str>,
313 referenced: bool,
314}
315
316impl VariableResolver for RuleColumnResolver<'_> {
317 fn resolve_name(&mut self, _name: &str) -> Result<Option<ResolvedVariable>, SQLError> {
318 Ok(None)
319 }
320
321 fn resolve_qualified(
322 &mut self,
323 _qualifier: &str,
324 _column: &str,
325 ) -> Result<Option<ResolvedVariable>, SQLError> {
326 Ok(None)
327 }
328
329 fn resolve_param(&mut self, _index: usize) -> Result<Option<ResolvedVariable>, SQLError> {
330 Ok(None)
331 }
332
333 fn rewrite_qualified(
334 &mut self,
335 qualifier: &str,
336 column: &str,
337 ) -> Result<Option<Expr>, SQLError> {
338 if (qualifier.eq_ignore_ascii_case("old") || qualifier.eq_ignore_ascii_case("new"))
339 && column == self.from
340 {
341 self.referenced = true;
342 if let Some(to) = self.to {
343 return Ok(Some(Expr::qualified_column(qualifier, to)));
344 }
345 }
346 Ok(None)
347 }
348}