Skip to main content

uqa_sql/catalog/events/
renames.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Rewrite bound relation identities in rule actions, conditions, and event catalogs.
8use super::{RuleCatalog, StoredRule, TriggerCatalog};
9use crate::{ast::Statement, catalog::stored_ast::StoredAstVisitor, SQLError};
10use std::collections::BTreeSet;
11use uqa_core::RelationIdentity;
12pub fn rewrite_stored_statement_relation(
13    statement: &mut Statement,
14    from: &RelationIdentity,
15    to: &RelationIdentity,
16) -> Result<bool, SQLError> {
17    let from = from.qualified_name();
18    let to = to.qualified_name();
19    let mut changed = false;
20    let mut rewrite = |reference: &mut String| {
21        if reference == &from {
22            reference.clone_from(&to);
23            changed = true;
24        }
25        Ok(())
26    };
27    let mut ignore_routine = |_: &mut String,
28                              _: Option<&mut Option<crate::ast::FunctionBinding>>|
29     -> Result<(), SQLError> { Ok(()) };
30    StoredAstVisitor {
31        source: None,
32        merge: None,
33        expression: None,
34        ty: None,
35        relation: &mut rewrite,
36        routine: &mut ignore_routine,
37    }
38    .bind_statement(statement)?;
39    Ok(changed)
40}
41
42pub fn rewrite_stored_rule_relation(
43    rule: &mut StoredRule,
44    from: &RelationIdentity,
45    to: &RelationIdentity,
46) -> Result<bool, SQLError> {
47    let dependencies = rule.dependencies.as_mut().ok_or_else(|| {
48        SQLError::Internal(format!(
49            "rule `{}` has no bound dependency state",
50            rule.definition.name
51        ))
52    })?;
53    let mut changed = false;
54    if dependencies.relations.remove(from) {
55        dependencies.relations.insert(to.clone());
56        changed = true;
57    }
58    let renamed_columns = dependencies
59        .columns
60        .iter()
61        .filter(|dependency| &dependency.relation == from)
62        .cloned()
63        .collect::<Vec<_>>();
64    for mut dependency in renamed_columns {
65        dependencies.columns.remove(&dependency);
66        dependency.relation = to.clone();
67        dependencies.columns.insert(dependency);
68        changed = true;
69    }
70    for action in &mut rule.definition.actions {
71        changed |= rewrite_stored_statement_relation(action, from, to)?;
72    }
73    if let Some(condition) = &mut rule.definition.condition {
74        let from_name = from.qualified_name();
75        let to_name = to.qualified_name();
76        let mut rewrite = |reference: &mut String| {
77            if reference == &from_name {
78                reference.clone_from(&to_name);
79                changed = true;
80            }
81            Ok(())
82        };
83        let mut ignore_routine = |_: &mut String,
84                                  _: Option<&mut Option<crate::ast::FunctionBinding>>|
85         -> Result<(), SQLError> { Ok(()) };
86        StoredAstVisitor {
87            source: None,
88            merge: None,
89            expression: None,
90            ty: None,
91            relation: &mut rewrite,
92            routine: &mut ignore_routine,
93        }
94        .bind_expr(condition, &BTreeSet::new())?;
95    }
96    if let Some(plan) = &mut rule.condition_plan {
97        for subquery in &mut plan.subqueries {
98            crate::binding::view_dependencies::bind_query_plan_relations(
99                subquery,
100                &BTreeSet::new(),
101                &mut |reference| -> Result<String, SQLError> {
102                    let identity =
103                        RelationIdentity::from_legacy_name(reference).map_err(|error| {
104                            SQLError::Internal(format!(
105                                "decode stored rule relation `{reference}`: {error}"
106                            ))
107                        })?;
108                    if &identity == from {
109                        changed = true;
110                        Ok(to.qualified_name())
111                    } else {
112                        Ok(reference.to_string())
113                    }
114                },
115            )?;
116        }
117    }
118    super::synchronize_rule_sql_text(&mut rule.definition)?;
119    Ok(changed)
120}
121
122pub fn renamed_relation_events(
123    triggers: &TriggerCatalog,
124    rules: &RuleCatalog,
125    from: &RelationIdentity,
126    to: &RelationIdentity,
127) -> Result<Option<(TriggerCatalog, RuleCatalog)>, String> {
128    let from_name = from.qualified_name();
129    let to_name = to.qualified_name();
130    let referenced_by_trigger = triggers.values().any(|entries| {
131        entries.values().any(|trigger| {
132            trigger.definition.referenced_table.as_deref() == Some(from_name.as_str())
133        })
134    });
135    let mut referenced_by_rule = false;
136    for (event_relation, entries) in rules {
137        for rule in entries.values() {
138            let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
139                format!(
140                    "rule `{}` on `{}` has no bound dependency state",
141                    rule.definition.name,
142                    event_relation.qualified_name()
143                )
144            })?;
145            referenced_by_rule |= dependencies.relations.contains(from);
146        }
147    }
148    if !triggers.contains_key(from)
149        && !rules.contains_key(from)
150        && !referenced_by_trigger
151        && !referenced_by_rule
152    {
153        return Ok(None);
154    }
155    let mut next_triggers = triggers.clone();
156    let mut next_rules = rules.clone();
157    if let Some(mut entries) = next_triggers.remove(from) {
158        for trigger in entries.values_mut() {
159            trigger.definition.table.clone_from(&to_name);
160        }
161        next_triggers.insert(to.clone(), entries);
162    }
163    for entries in next_triggers.values_mut() {
164        for trigger in entries.values_mut() {
165            if trigger.definition.referenced_table.as_deref() == Some(from_name.as_str()) {
166                trigger.definition.referenced_table = Some(to_name.clone());
167            }
168        }
169    }
170    if let Some(mut entries) = next_rules.remove(from) {
171        for rule in entries.values_mut() {
172            rule.definition.table = to.qualified_name();
173        }
174        next_rules.insert(to.clone(), entries);
175    }
176    for entries in next_rules.values_mut() {
177        for rule in entries.values_mut() {
178            rewrite_stored_rule_relation(rule, from, to).map_err(|error| {
179                format!(
180                    "rewrite rule `{}` relation dependency: {error}",
181                    rule.definition.name
182                )
183            })?;
184        }
185    }
186    Ok(Some((next_triggers, next_rules)))
187}