Skip to main content

uqa_sql/catalog/
events.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use crate::ast::{CreateRule, CreateTrigger, EventEnableMode};
8use serde::{Deserialize, Serialize};
9use std::collections::BTreeSet;
10
11mod rule_condition_binding;
12pub use rule_condition_binding::RuleConditionBinding;
13
14#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
15pub struct RuleDependencies {
16    #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
17    pub relations: BTreeSet<uqa_core::RelationIdentity>,
18    #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
19    pub columns: BTreeSet<RuleColumnDependency>,
20    #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
21    pub routines: BTreeSet<RuleRoutineDependency>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
25pub struct RuleColumnDependency {
26    pub relation: uqa_core::RelationIdentity,
27    pub column: String,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
31pub struct RuleRoutineDependency {
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub object_id: Option<[u8; 16]>,
34    pub name: String,
35    pub argument_types: Vec<String>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct StoredTrigger {
40    pub definition: CreateTrigger,
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub function_object_id: Option<[u8; 16]>,
43    #[serde(default)]
44    pub enabled: EventEnableMode,
45    #[serde(default)]
46    pub object_id: Option<[u8; 16]>,
47    #[serde(default)]
48    pub constraint_name: Option<String>,
49    /// The `pg_trigger` OID `CreateTrigger` allocated; triggers created before OIDs were recorded derive theirs from their identity and relation.
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub catalog_oid: Option<i64>,
52    /// The `pg_constraint` OID of a constraint trigger, allocated after the trigger's.
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub constraint_catalog_oid: Option<i64>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct StoredRule {
59    pub definition: CreateRule,
60    #[serde(default)]
61    pub enabled: EventEnableMode,
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub condition_plan: Option<crate::plan::ExpressionPlan>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub condition_binding: Option<RuleConditionBinding>,
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub dependencies: Option<RuleDependencies>,
68    /// The `pg_rewrite` OID `InsertRule` allocated; rules created before OIDs were recorded keep the OID their name derived when the catalog first opened with this field.
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub catalog_oid: Option<i64>,
71}
72
73impl StoredRule {
74    pub fn bound_condition_plan(
75        &self,
76    ) -> Option<(&crate::plan::ExpressionPlan, &RuleConditionBinding)> {
77        self.condition_plan
78            .as_ref()
79            .zip(self.condition_binding.as_ref())
80    }
81}
82
83impl StoredTrigger {
84    pub fn constraint_identity(
85        &self,
86    ) -> Result<super::constraints::ConstraintIdentity, crate::SQLError> {
87        use super::constraints::ConstraintIdentity;
88        use crate::SQLError;
89        use uqa_core::RelationIdentity;
90        let trigger = self;
91        if !trigger.definition.constraint {
92            return Err(SQLError::Internal(format!(
93                "ordinary trigger `{}` requested a constraint identity",
94                trigger.definition.name
95            )));
96        }
97        let relation =
98            RelationIdentity::from_legacy_name(&trigger.definition.table).map_err(|error| {
99                SQLError::Internal(format!(
100                    "decode constraint-trigger relation `{}`: {error}",
101                    trigger.definition.table
102                ))
103            })?;
104        Ok(ConstraintIdentity {
105            relation,
106            name: trigger
107                .constraint_name
108                .clone()
109                .unwrap_or_else(|| trigger.definition.name.clone()),
110            object_id: trigger.object_id,
111        })
112    }
113}
114
115pub type TriggerCatalog = std::collections::BTreeMap<
116    uqa_core::RelationIdentity,
117    std::collections::BTreeMap<String, StoredTrigger>,
118>;
119pub type RuleCatalog = std::collections::BTreeMap<
120    uqa_core::RelationIdentity,
121    std::collections::BTreeMap<String, StoredRule>,
122>;
123pub mod dependencies;
124pub mod renames;
125
126/// Surviving rule definitions prepared before column metadata changes, plus the rules to rebind afterward.
127pub struct PreparedRuleColumnDrop {
128    pub rules: RuleCatalog,
129    pub rebind: BTreeSet<(uqa_core::RelationIdentity, String)>,
130}
131
132pub mod validation;
133
134pub mod definition;
135
136pub mod restoration;
137
138pub mod reads;
139
140pub mod removal;
141
142pub mod persistence;