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 rules_depending_on_relations(
21 &self,
22 relations: &[String],
23 ) -> Result<Vec<(RelationIdentity, String)>, String> {
24 let targets = relations
25 .iter()
26 .map(|relation| RelationIdentity::from_legacy_name(relation))
27 .collect::<Result<std::collections::BTreeSet<_>, String>>()?;
28 let rules = self.registry.read_rules();
29 let mut dependents = Vec::new();
30 for (event_relation, entries) in rules.iter() {
31 if targets.contains(event_relation) {
32 continue;
33 }
34 for rule in entries.values() {
35 let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
36 format!(
37 "rule `{}` on `{}` has no bound dependency state",
38 rule.definition.name,
39 event_relation.qualified_name()
40 )
41 })?;
42 if dependencies
43 .relations
44 .iter()
45 .any(|dependency| targets.contains(dependency))
46 {
47 dependents.push((event_relation.clone(), rule.definition.name.clone()));
48 }
49 }
50 }
51 dependents.sort();
52 Ok(dependents)
53 }
54
55 pub fn rules_depending_on_routine(
56 &self,
57 target: &crate::ast::FunctionBinding,
58 ) -> Result<Vec<(RelationIdentity, String)>, String> {
59 let rules = self.registry.read_rules();
60 let mut dependents = Vec::new();
61 for (event_relation, entries) in rules.iter() {
62 for rule in entries.values() {
63 let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
64 format!(
65 "rule `{}` on `{}` has no bound dependency state",
66 rule.definition.name,
67 event_relation.qualified_name()
68 )
69 })?;
70 if dependencies.routines.iter().any(|dependency| {
71 match (dependency.object_id, target.object_id) {
72 (Some(dependency), Some(target)) => dependency == target,
73 (None, None) => {
74 dependency.name == target.name
75 && dependency.argument_types == target.argument_types
76 }
77 _ => false,
78 }
79 }) {
80 dependents.push((event_relation.clone(), rule.definition.name.clone()));
81 }
82 }
83 }
84 dependents.sort();
85 Ok(dependents)
86 }
87
88 pub fn triggers_depending_on_routine(
89 &self,
90 target: &crate::ast::FunctionBinding,
91 ) -> Result<Vec<(String, String)>, SQLError> {
92 let mut dependents = Vec::new();
93 for trigger in self.list_triggers() {
94 let invokes_target = match (trigger.function_object_id, target.object_id) {
95 (Some(trigger), Some(target)) => trigger == target,
96 (None, None) => {
97 target.argument_types.is_empty() && trigger.definition.function == target.name
98 }
99 _ => false,
100 };
101 let condition_references_target = trigger
102 .definition
103 .when
104 .as_ref()
105 .map(|condition| {
106 crate::catalog::stored_ast::expression_references_routine_identity(
107 condition, target,
108 )
109 })
110 .transpose()?
111 .unwrap_or(false);
112 if invokes_target || condition_references_target {
113 dependents.push((
114 trigger.definition.table.clone(),
115 trigger.definition.name.clone(),
116 ));
117 }
118 }
119 dependents.sort();
120 Ok(dependents)
121 }
122
123 pub fn prepare_rule_column_drop(
124 &self,
125 table: &str,
126 column: &str,
127 ) -> Result<PreparedRuleColumnDrop, String> {
128 let relation = RelationIdentity::from_legacy_name(table)?;
129 let dependency = RuleColumnDependency {
130 relation: relation.clone(),
131 column: column.to_string(),
132 };
133 let mut rules = self.registry.read_rules().clone();
134 let mut rebind = std::collections::BTreeSet::new();
135 for (event_relation, entries) in &mut rules {
136 for (name, rule) in entries {
137 let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
138 format!(
139 "rule `{}` on `{}` has no bound dependency state",
140 rule.definition.name,
141 event_relation.qualified_name()
142 )
143 })?;
144 if dependencies.columns.contains(&dependency) {
145 return Err(format!(
146 "cannot drop column {column} of table {table} because rule {} on {} depends on it",
147 rule.definition.name,
148 event_relation.qualified_name()
149 ));
150 }
151 if event_relation != &relation && !dependencies.relations.contains(&relation) {
152 continue;
153 }
154 crate::binding::stored_columns::remove_rule_source_column_aliases(
155 self.analysis.columns,
156 &mut rule.definition,
157 &dependency,
158 )
159 .map_err(|error| {
160 format!(
161 "reshape rule `{}` source aliases before column drop: {error}",
162 rule.definition.name
163 )
164 })?;
165 rebind.insert((event_relation.clone(), name.clone()));
166 }
167 }
168 Ok(PreparedRuleColumnDrop { rules, rebind })
169 }
170
171 pub fn column_event_dependencies(
172 &self,
173 table: &str,
174 column: &str,
175 ) -> Result<EventColumnDependencies, SQLError> {
176 let relation = RelationIdentity::from_legacy_name(table).map_err(|error| {
177 SQLError::Internal(format!("decode trigger relation `{table}`: {error}"))
178 })?;
179 let dependent_triggers = self
180 .registry
181 .read_triggers()
182 .get(&relation)
183 .into_iter()
184 .flat_map(BTreeMap::values)
185 .filter(|trigger| {
186 trigger
187 .definition
188 .update_columns
189 .iter()
190 .any(|name| name == column)
191 || trigger.definition.when.as_ref().is_some_and(|condition| {
192 crate::schema::dependencies::schema_expr_references_column(
193 condition, column,
194 )
195 })
196 })
197 .map(|trigger| trigger.definition.name.clone())
198 .collect::<Vec<_>>();
199 let rules = self.registry.read_rules();
200 let dependent_rules = dependent_rules_for_column(&rules, &relation, column)?;
201 drop(rules);
202 Ok(EventColumnDependencies {
203 triggers: dependent_triggers,
204 rules: dependent_rules,
205 })
206 }
207}
208
209fn dependent_rules_for_column(
210 rules: &BTreeMap<RelationIdentity, BTreeMap<String, StoredRule>>,
211 relation: &RelationIdentity,
212 column: &str,
213) -> Result<Vec<(RelationIdentity, String)>, SQLError> {
214 let dependency = RuleColumnDependency {
215 relation: relation.clone(),
216 column: column.to_string(),
217 };
218 let mut dependent = Vec::new();
219 for (event_relation, entries) in rules {
220 for rule in entries.values() {
221 let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
222 SQLError::Internal(format!(
223 "rule `{}` on `{}` has no bound dependency state",
224 rule.definition.name,
225 event_relation.qualified_name()
226 ))
227 })?;
228 if dependencies.columns.contains(&dependency) {
229 dependent.push((event_relation.clone(), rule.definition.name.clone()));
230 }
231 }
232 }
233 Ok(dependent)
234}