Skip to main content

uqa_sql/catalog/events/
removal.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Candidate event catalogs and constraint identities for relation and dependency removal.
8use super::{RuleCatalog, TriggerCatalog};
9use crate::catalog::constraints::ConstraintIdentity;
10use std::collections::BTreeMap;
11use uqa_core::RelationIdentity;
12pub struct RemovedRelationEvents {
13    pub triggers: TriggerCatalog,
14    pub rules: RuleCatalog,
15    pub constraints: Vec<ConstraintIdentity>,
16}
17pub fn removed_relation_events(
18    triggers: &TriggerCatalog,
19    rules: &RuleCatalog,
20    relation: &RelationIdentity,
21) -> Result<Option<RemovedRelationEvents>, String> {
22    let qualified = relation.qualified_name();
23    let referenced_by_trigger = triggers.values().any(|entries| {
24        entries.values().any(|trigger| {
25            trigger.definition.referenced_table.as_deref() == Some(qualified.as_str())
26        })
27    });
28    if !triggers.contains_key(relation) && !rules.contains_key(relation) && !referenced_by_trigger {
29        return Ok(None);
30    }
31    let mut next_triggers = triggers.clone();
32    let mut next_rules = rules.clone();
33    let mut removed_constraint_identities = Vec::new();
34    for (trigger_relation, entries) in triggers {
35        for trigger in entries.values() {
36            if trigger.definition.constraint
37                && (trigger_relation == relation
38                    || trigger.definition.referenced_table.as_deref() == Some(qualified.as_str()))
39            {
40                removed_constraint_identities.push(trigger.constraint_identity().map_err(
41                    |error| format!("resolve dropped constraint-trigger identity: {error}"),
42                )?);
43            }
44        }
45    }
46    next_triggers.remove(relation);
47    for entries in next_triggers.values_mut() {
48        entries.retain(|_, trigger| {
49            trigger.definition.referenced_table.as_deref() != Some(qualified.as_str())
50        });
51    }
52    next_triggers.retain(|_, entries| !entries.is_empty());
53    next_rules.remove(relation);
54
55    Ok(Some(RemovedRelationEvents {
56        triggers: next_triggers,
57        rules: next_rules,
58        constraints: removed_constraint_identities,
59    }))
60}
61pub fn removed_dependent_rules(
62    rules: &RuleCatalog,
63    dependents: &[(RelationIdentity, String)],
64) -> Result<RuleCatalog, String> {
65    let mut next = rules.clone();
66    for (event_relation, name) in dependents {
67        let removed = next
68            .get_mut(event_relation)
69            .and_then(|entries| entries.remove(name));
70        if removed.is_none() {
71            return Err(format!(
72                "dependent rule `{name}` on `{}` disappeared after DROP preflight",
73                event_relation.qualified_name()
74            ));
75        }
76        if next.get(event_relation).is_some_and(BTreeMap::is_empty) {
77            next.remove(event_relation);
78        }
79    }
80
81    Ok(next)
82}