uqa_sql/catalog/events/definition/
dependents.rs1use super::lookup::EventLookupContext;
9use crate::{
10 catalog::events::{PreparedRuleColumnDrop, RuleColumnDependency, StoredRule},
11 SQLError,
12};
13use std::collections::BTreeMap;
14use uqa_core::RelationIdentity;
15pub struct EventColumnDependencies {
16 pub triggers: Vec<String>,
17 pub rules: Vec<(RelationIdentity, String)>,
18}
19impl EventLookupContext<'_> {
20 pub fn prepare_rule_column_drop(
21 &self,
22 table: &str,
23 column: &str,
24 ) -> Result<PreparedRuleColumnDrop, String> {
25 let relation = RelationIdentity::from_legacy_name(table)?;
26 let dependency = RuleColumnDependency {
27 relation: relation.clone(),
28 column: column.to_string(),
29 };
30 let mut rules = self.registry.read_rules().clone();
31 let mut rebind = std::collections::BTreeSet::new();
32 for (event_relation, entries) in &mut rules {
33 for (name, rule) in entries {
34 let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
35 format!(
36 "rule `{}` on `{}` has no bound dependency state",
37 rule.definition.name,
38 event_relation.qualified_name()
39 )
40 })?;
41 if dependencies.columns.contains(&dependency) {
42 return Err(format!(
43 "cannot drop column {column} of table {table} because rule {} on {} depends on it",
44 rule.definition.name,
45 event_relation.qualified_name()
46 ));
47 }
48 if event_relation != &relation && !dependencies.relations.contains(&relation) {
49 continue;
50 }
51 crate::binding::stored_columns::remove_rule_source_column_aliases(
52 self.analysis.columns,
53 &mut rule.definition,
54 &dependency,
55 )
56 .map_err(|error| {
57 format!(
58 "reshape rule `{}` source aliases before column drop: {error}",
59 rule.definition.name
60 )
61 })?;
62 rebind.insert((event_relation.clone(), name.clone()));
63 }
64 }
65 Ok(PreparedRuleColumnDrop { rules, rebind })
66 }
67
68 pub fn column_event_dependencies(
69 &self,
70 table: &str,
71 column: &str,
72 ) -> Result<EventColumnDependencies, SQLError> {
73 let relation = RelationIdentity::from_legacy_name(table).map_err(|error| {
74 SQLError::Internal(format!("decode trigger relation `{table}`: {error}"))
75 })?;
76 let dependent_triggers = self
77 .registry
78 .read_triggers()
79 .get(&relation)
80 .into_iter()
81 .flat_map(BTreeMap::values)
82 .filter(|trigger| {
83 trigger
84 .definition
85 .update_columns
86 .iter()
87 .any(|name| name == column)
88 || trigger.definition.when.as_ref().is_some_and(|condition| {
89 crate::schema::dependencies::schema_expr_references_column(
90 condition, column,
91 )
92 })
93 })
94 .map(|trigger| trigger.definition.name.clone())
95 .collect::<Vec<_>>();
96 let rules = self.registry.read_rules();
97 let dependent_rules = dependent_rules_for_column(&rules, &relation, column)?;
98 drop(rules);
99 Ok(EventColumnDependencies {
100 triggers: dependent_triggers,
101 rules: dependent_rules,
102 })
103 }
104}
105
106fn dependent_rules_for_column(
107 rules: &BTreeMap<RelationIdentity, BTreeMap<String, StoredRule>>,
108 relation: &RelationIdentity,
109 column: &str,
110) -> Result<Vec<(RelationIdentity, String)>, SQLError> {
111 let dependency = RuleColumnDependency {
112 relation: relation.clone(),
113 column: column.to_string(),
114 };
115 let mut dependent = Vec::new();
116 for (event_relation, entries) in rules {
117 for rule in entries.values() {
118 let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
119 SQLError::Internal(format!(
120 "rule `{}` on `{}` has no bound dependency state",
121 rule.definition.name,
122 event_relation.qualified_name()
123 ))
124 })?;
125 if dependencies.columns.contains(&dependency) {
126 dependent.push((event_relation.clone(), rule.definition.name.clone()));
127 }
128 }
129 }
130 Ok(dependent)
131}