Skip to main content

uqa_sql/catalog/
system_relations.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Built-in relation identity, bootstrap security and ordered view references.
8
9use super::{
10    security::{table_binding::BoundTableAclEntry, BoundTableSecurity, TablePrivileges},
11    VirtualRelation,
12};
13mod columns;
14
15macro_rules! system_relations {
16    ($($variant:ident => ($schema:literal, $name:literal, $oid:literal, $kind:literal)),* $(,)?) => {
17        /// Catalog identity is independent of whether a virtual row projector is available.
18        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
19        pub enum SystemRelation {
20            Projected(VirtualRelation),
21            $($variant),*
22        }
23
24        impl SystemRelation {
25            pub fn all() -> impl Iterator<Item = Self> {
26                VirtualRelation::ALL.iter().copied().map(Self::Projected)
27                    .chain([$(Self::$variant),*])
28            }
29
30            pub const fn namespace(self) -> &'static str {
31                match self {
32                    Self::Projected(relation) => relation.namespace(),
33                    $(Self::$variant => $schema),*
34                }
35            }
36
37            pub const fn name(self) -> &'static str {
38                match self {
39                    Self::Projected(relation) => relation.name(),
40                    $(Self::$variant => $name),*
41                }
42            }
43
44            pub fn oid(self) -> i64 {
45                match self {
46                    Self::Projected(relation) => relation.oid(),
47                    $(Self::$variant => $oid),*
48                }
49            }
50
51            pub const fn kind(self) -> &'static str {
52                match self {
53                    Self::Projected(relation) => relation.kind(),
54                    $(Self::$variant => $kind),*
55                }
56            }
57
58            pub fn at(schema: &str, name: &str) -> Option<Self> {
59                VirtualRelation::at(schema, name).map(Self::Projected).or_else(|| {
60                    match (schema, name) {
61                        $(($schema, $name) => Some(Self::$variant),)*
62                        _ => None,
63                    }
64                })
65            }
66        }
67    };
68}
69
70system_relations! {
71    PgDbRoleSetting => ("pg_catalog", "pg_db_role_setting", 2964, "table"),
72    PgShadow => ("pg_catalog", "pg_shadow", 12005, "view"),
73    PgTablespace => ("pg_catalog", "pg_tablespace", 1213, "table"),
74    PgCollation => ("pg_catalog", "pg_collation", 3456, "table"),
75    PgDepend => ("pg_catalog", "pg_depend", 2608, "table"),
76    PgSequence => ("pg_catalog", "pg_sequence", 2224, "table"),
77    PgLanguage => ("pg_catalog", "pg_language", 2612, "table"),
78    InformationEnabledRoles => ("information_schema", "enabled_roles", 13410, "view"),
79}
80
81impl SystemRelation {
82    /// `PostgreSQL`'s pinned system catalogs and their metadata views are outside SSI. AGE catalogs are ordinary extension tables and retain data predicates.
83    pub const fn tracks_serializable_reads(self) -> bool {
84        matches!(
85            self,
86            Self::Projected(VirtualRelation::AgGraph | VirtualRelation::AgLabel)
87        )
88    }
89
90    pub fn qualified_name(self) -> String {
91        format!("{}.{}", self.namespace(), self.name())
92    }
93
94    pub fn from_qualified_name(name: &str) -> Option<Self> {
95        let (schema, local) = uqa_core::RelationIdentity::parse_reference(name).ok()?;
96        Self::at(schema.as_deref()?, &local)
97    }
98
99    /// Immutable built-ins retain the same identity in every session; lock managers retain database affinity.
100    pub fn object_id(self) -> [u8; 16] {
101        let mut identity = [0; 16];
102        identity[..8].copy_from_slice(b"UQA:cat:");
103        identity[8..].copy_from_slice(&self.oid().to_be_bytes());
104        identity
105    }
106
107    pub fn bootstrap_security(self) -> BoundTableSecurity {
108        let mut security = BoundTableSecurity::owner(super::roles::RoleIdentity::BOOTSTRAP);
109        let mut acl = vec![BoundTableAclEntry {
110            role: Some(security.role_owner),
111            grantor: security.role_owner,
112            privileges: TablePrivileges::ALL,
113            grant_options: TablePrivileges::default(),
114        }];
115        if !matches!(
116            self,
117            Self::PgShadow
118                | Self::Projected(
119                    VirtualRelation::PgAuthid | VirtualRelation::AgGraph | VirtualRelation::AgLabel
120                )
121        ) {
122            acl.push(BoundTableAclEntry {
123                role: None,
124                grantor: security.role_owner,
125                privileges: TablePrivileges {
126                    select: true,
127                    update: self == Self::Projected(VirtualRelation::PgSettings),
128                    ..TablePrivileges::default()
129                },
130                grant_options: TablePrivileges::default(),
131            });
132        }
133        security.acl = Some(acl);
134        security
135    }
136
137    /// `PostgreSQL` 18 view range-table order, followed by expression and range-table subqueries. Repeated references preserve that order; consumers own recursion and lock retention.
138    pub const fn view_sources(self) -> &'static [Self] {
139        use SystemRelation::Projected as P;
140        use VirtualRelation::{
141            InformationColumnPrivileges, PgAttrdef, PgAttribute, PgClass, PgConstraint, PgIndex,
142            PgNamespace, PgProc, PgRewrite, PgTrigger, PgType,
143        };
144        match self {
145            P(VirtualRelation::InformationSchemata) => {
146                &[P(PgNamespace), Self::Projected(VirtualRelation::PgAuthid)]
147            }
148            P(VirtualRelation::InformationTables) => {
149                &[P(PgNamespace), P(PgClass), P(PgType), P(PgNamespace)]
150            }
151            P(VirtualRelation::InformationColumns) => &[
152                P(PgAttribute),
153                P(PgAttrdef),
154                P(PgClass),
155                P(PgNamespace),
156                P(PgType),
157                P(PgNamespace),
158                P(PgType),
159                P(PgNamespace),
160                Self::PgCollation,
161                P(PgNamespace),
162                Self::PgDepend,
163                Self::PgSequence,
164            ],
165            P(InformationColumnPrivileges) => &[
166                P(PgNamespace),
167                Self::Projected(VirtualRelation::PgAuthid),
168                P(PgAttribute),
169                P(PgClass),
170                P(PgClass),
171                P(PgAttribute),
172                P(PgClass),
173                Self::Projected(VirtualRelation::PgAuthid),
174            ],
175            P(VirtualRelation::InformationRoleColumnGrants) => &[
176                P(InformationColumnPrivileges),
177                Self::InformationEnabledRoles,
178                Self::InformationEnabledRoles,
179            ],
180            P(VirtualRelation::InformationViews) => &[
181                P(PgNamespace),
182                P(PgClass),
183                P(PgTrigger),
184                P(PgTrigger),
185                P(PgTrigger),
186            ],
187            P(VirtualRelation::InformationRoutines) => &[
188                P(PgNamespace),
189                P(PgProc),
190                Self::PgLanguage,
191                P(PgType),
192                P(PgNamespace),
193            ],
194            P(VirtualRelation::InformationSequences) => {
195                &[P(PgNamespace), P(PgClass), Self::PgSequence, Self::PgDepend]
196            }
197            P(VirtualRelation::InformationTableConstraints) => &[
198                P(PgNamespace),
199                P(PgNamespace),
200                P(PgConstraint),
201                P(PgClass),
202                P(PgIndex),
203            ],
204            P(VirtualRelation::InformationKeyColumnUsage) => &[
205                P(PgAttribute),
206                P(PgNamespace),
207                P(PgClass),
208                P(PgNamespace),
209                P(PgConstraint),
210            ],
211            P(VirtualRelation::PgRules) => &[P(PgRewrite), P(PgClass), P(PgNamespace)],
212            P(VirtualRelation::PgTables | VirtualRelation::PgMatviews) => {
213                &[P(PgClass), P(PgNamespace), Self::PgTablespace]
214            }
215            P(VirtualRelation::PgViews) => &[P(PgClass), P(PgNamespace)],
216            P(VirtualRelation::PgIndexes) => &[
217                P(PgIndex),
218                P(PgClass),
219                P(PgClass),
220                P(PgNamespace),
221                Self::PgTablespace,
222            ],
223            P(VirtualRelation::PgRoles) | Self::PgShadow => &[
224                Self::Projected(VirtualRelation::PgAuthid),
225                Self::PgDbRoleSetting,
226            ],
227            P(VirtualRelation::PgUser) => &[Self::PgShadow],
228            P(VirtualRelation::PgSequences) => &[Self::PgSequence, P(PgClass), P(PgNamespace)],
229            Self::InformationEnabledRoles => &[Self::Projected(VirtualRelation::PgAuthid)],
230            _ => &[],
231        }
232    }
233}
234
235#[cfg(test)]
236mod tests;