Skip to main content

uqa_sql/catalog/events/
definition.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Trigger and rewrite-rule declarations, relation identity checks, and stored expression binding.
8
9mod context;
10mod rules;
11mod triggers;
12use crate::{
13    catalog::resolution::{RelationLookupMode, RelationResolution},
14    SQLError,
15};
16pub use context::{EventAnalysisContext, EventForeignPrivileges, EventRelationCatalog};
17use uqa_core::RelationIdentity;
18
19impl EventAnalysisContext<'_> {
20    pub fn event_relation_from_resolution(
21        requested: &str,
22        resolution: RelationResolution,
23    ) -> Result<(RelationIdentity, &'static str), SQLError> {
24        let (canonical, kind) = match resolution {
25            RelationResolution::Found(canonical, kind)
26                if matches!(kind, "table" | "view" | "materialized view") =>
27            {
28                (canonical, kind)
29            }
30            RelationResolution::Found(_, _) | RelationResolution::MissingRelation => {
31                return Err(SQLError::UnknownTable(requested.to_string()));
32            }
33            RelationResolution::MissingSchema(schema) => {
34                return Err(SQLError::Routine {
35                    sqlstate: "3F000".into(),
36                    message: format!("schema \"{schema}\" does not exist"),
37                });
38            }
39        };
40        let relation = RelationIdentity::from_legacy_name(&canonical).map_err(|error| {
41            SQLError::Internal(format!(
42                "decode resolved event relation `{canonical}`: {error}"
43            ))
44        })?;
45        Ok((relation, kind))
46    }
47
48    pub fn resolve_event_relation_kind(
49        &self,
50        name: &str,
51        lookup_mode: RelationLookupMode,
52    ) -> Result<(RelationIdentity, &'static str), SQLError> {
53        let resolution = match lookup_mode {
54            RelationLookupMode::Dynamic => self.relations.resolve_visible_relation_kind(name)?,
55            RelationLookupMode::Bound => self.relations.resolve_bound_relation_kind(name)?,
56        };
57        Self::event_relation_from_resolution(name, resolution)
58    }
59}
60
61impl EventAnalysisContext<'_> {
62    pub fn ensure_event_relation_owner(
63        &self,
64        relation: &RelationIdentity,
65        error_kind: Option<&str>,
66    ) -> Result<(), SQLError> {
67        let (owner, relation_kind) = self.catalog.event_relation_owner(relation)?;
68        if self.authority.current_user_has_role_privileges(&owner) {
69            return Ok(());
70        }
71        Err(SQLError::Routine {
72            sqlstate: "42501".into(),
73            message: format!(
74                "must be owner of {} {}",
75                error_kind.unwrap_or(relation_kind),
76                relation.name
77            ),
78        })
79    }
80}
81
82#[cfg(test)]
83mod tests;
84
85pub fn duplicate_object(kind: &str, name: &str, table: &str) -> SQLError {
86    SQLError::Routine {
87        sqlstate: "42710".into(),
88        message: format!("{kind} \"{name}\" for relation \"{table}\" already exists"),
89    }
90}
91
92pub fn undefined_object(kind: &str, name: &str, table: &str) -> SQLError {
93    SQLError::Routine {
94        sqlstate: "42704".into(),
95        message: format!("{kind} \"{name}\" for table \"{table}\" does not exist"),
96    }
97}
98
99pub fn undefined_rule(name: &str, relation: &str) -> SQLError {
100    SQLError::Routine {
101        sqlstate: "42704".into(),
102        message: format!("rule \"{name}\" for relation \"{relation}\" does not exist"),
103    }
104}
105
106pub mod lookup;
107
108pub mod dependents;
109pub mod rewrites;