use super::lookup::EventLookupContext;
use crate::{
catalog::events::{PreparedRuleColumnDrop, RuleColumnDependency, StoredRule},
SQLError,
};
use std::collections::BTreeMap;
use uqa_core::RelationIdentity;
pub struct EventColumnDependencies {
pub triggers: Vec<String>,
pub rules: Vec<(RelationIdentity, String)>,
}
impl EventLookupContext<'_> {
pub fn prepare_rule_column_drop(
&self,
table: &str,
column: &str,
) -> Result<PreparedRuleColumnDrop, String> {
let relation = RelationIdentity::from_legacy_name(table)?;
let dependency = RuleColumnDependency {
relation: relation.clone(),
column: column.to_string(),
};
let mut rules = self.registry.read_rules().clone();
let mut rebind = std::collections::BTreeSet::new();
for (event_relation, entries) in &mut rules {
for (name, rule) in entries {
let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
format!(
"rule `{}` on `{}` has no bound dependency state",
rule.definition.name,
event_relation.qualified_name()
)
})?;
if dependencies.columns.contains(&dependency) {
return Err(format!(
"cannot drop column {column} of table {table} because rule {} on {} depends on it",
rule.definition.name,
event_relation.qualified_name()
));
}
if event_relation != &relation && !dependencies.relations.contains(&relation) {
continue;
}
crate::binding::stored_columns::remove_rule_source_column_aliases(
self.analysis.columns,
&mut rule.definition,
&dependency,
)
.map_err(|error| {
format!(
"reshape rule `{}` source aliases before column drop: {error}",
rule.definition.name
)
})?;
rebind.insert((event_relation.clone(), name.clone()));
}
}
Ok(PreparedRuleColumnDrop { rules, rebind })
}
pub fn column_event_dependencies(
&self,
table: &str,
column: &str,
) -> Result<EventColumnDependencies, SQLError> {
let relation = RelationIdentity::from_legacy_name(table).map_err(|error| {
SQLError::Internal(format!("decode trigger relation `{table}`: {error}"))
})?;
let dependent_triggers = self
.registry
.read_triggers()
.get(&relation)
.into_iter()
.flat_map(BTreeMap::values)
.filter(|trigger| {
trigger
.definition
.update_columns
.iter()
.any(|name| name == column)
|| trigger.definition.when.as_ref().is_some_and(|condition| {
crate::schema::dependencies::schema_expr_references_column(
condition, column,
)
})
})
.map(|trigger| trigger.definition.name.clone())
.collect::<Vec<_>>();
let rules = self.registry.read_rules();
let dependent_rules = dependent_rules_for_column(&rules, &relation, column)?;
drop(rules);
Ok(EventColumnDependencies {
triggers: dependent_triggers,
rules: dependent_rules,
})
}
}
fn dependent_rules_for_column(
rules: &BTreeMap<RelationIdentity, BTreeMap<String, StoredRule>>,
relation: &RelationIdentity,
column: &str,
) -> Result<Vec<(RelationIdentity, String)>, SQLError> {
let dependency = RuleColumnDependency {
relation: relation.clone(),
column: column.to_string(),
};
let mut dependent = Vec::new();
for (event_relation, entries) in rules {
for rule in entries.values() {
let dependencies = rule.dependencies.as_ref().ok_or_else(|| {
SQLError::Internal(format!(
"rule `{}` on `{}` has no bound dependency state",
rule.definition.name,
event_relation.qualified_name()
))
})?;
if dependencies.columns.contains(&dependency) {
dependent.push((event_relation.clone(), rule.definition.name.clone()));
}
}
}
Ok(dependent)
}