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    PgSequence => ("pg_catalog", "pg_sequence", 2224, "table"),
76    InformationEnabledRoles => ("information_schema", "enabled_roles", 13410, "view"),
77}
78
79impl SystemRelation {
80    /// `PostgreSQL`'s pinned system catalogs and their metadata views are outside SSI. AGE catalogs are ordinary extension tables and retain data predicates.
81    pub const fn tracks_serializable_reads(self) -> bool {
82        matches!(
83            self,
84            Self::Projected(VirtualRelation::AgGraph | VirtualRelation::AgLabel)
85        )
86    }
87
88    pub fn qualified_name(self) -> String {
89        format!("{}.{}", self.namespace(), self.name())
90    }
91
92    pub fn from_qualified_name(name: &str) -> Option<Self> {
93        let (schema, local) = uqa_core::RelationIdentity::parse_reference(name).ok()?;
94        Self::at(schema.as_deref()?, &local)
95    }
96
97    /// Immutable built-ins retain the same identity in every session; lock managers retain database affinity.
98    pub fn object_id(self) -> [u8; 16] {
99        let mut identity = [0; 16];
100        identity[..8].copy_from_slice(b"UQA:cat:");
101        identity[8..].copy_from_slice(&self.oid().to_be_bytes());
102        identity
103    }
104
105    pub fn bootstrap_security(self) -> BoundTableSecurity {
106        let mut security = BoundTableSecurity::owner(super::roles::RoleIdentity::BOOTSTRAP);
107        let mut acl = vec![BoundTableAclEntry {
108            role: Some(security.role_owner),
109            grantor: security.role_owner,
110            privileges: TablePrivileges::ALL,
111            grant_options: TablePrivileges::default(),
112        }];
113        if !matches!(
114            self,
115            Self::PgShadow
116                | Self::Projected(
117                    VirtualRelation::PgAuthid | VirtualRelation::AgGraph | VirtualRelation::AgLabel
118                )
119        ) {
120            acl.push(BoundTableAclEntry {
121                role: None,
122                grantor: security.role_owner,
123                privileges: TablePrivileges {
124                    select: true,
125                    update: self == Self::Projected(VirtualRelation::PgSettings),
126                    ..TablePrivileges::default()
127                },
128                grant_options: TablePrivileges::default(),
129            });
130        }
131        security.acl = Some(acl);
132        security
133    }
134
135    /// `PostgreSQL` 18 view range-table order, followed by expression and range-table subqueries. Repeated references preserve that order; consumers own recursion and lock retention.
136    pub const fn view_sources(self) -> &'static [Self] {
137        use SystemRelation::Projected as P;
138        use VirtualRelation::{
139            InformationColumnPrivileges, PgAttrdef, PgAttribute, PgClass, PgConstraint, PgIndex,
140            PgLanguage, PgNamespace, PgProc, PgRewrite, PgTrigger, PgType,
141        };
142        match self {
143            P(VirtualRelation::InformationSchemata) => {
144                &[P(PgNamespace), Self::Projected(VirtualRelation::PgAuthid)]
145            }
146            P(VirtualRelation::InformationTables) => {
147                &[P(PgNamespace), P(PgClass), P(PgType), P(PgNamespace)]
148            }
149            P(VirtualRelation::InformationColumns) => &[
150                P(PgAttribute),
151                P(PgAttrdef),
152                P(PgClass),
153                P(PgNamespace),
154                P(PgType),
155                P(PgNamespace),
156                P(PgType),
157                P(PgNamespace),
158                Self::PgCollation,
159                P(PgNamespace),
160                P(VirtualRelation::PgDepend),
161                Self::PgSequence,
162            ],
163            P(InformationColumnPrivileges) => &[
164                P(PgNamespace),
165                Self::Projected(VirtualRelation::PgAuthid),
166                P(PgAttribute),
167                P(PgClass),
168                P(PgClass),
169                P(PgAttribute),
170                P(PgClass),
171                Self::Projected(VirtualRelation::PgAuthid),
172            ],
173            P(VirtualRelation::InformationRoleColumnGrants) => &[
174                P(InformationColumnPrivileges),
175                Self::InformationEnabledRoles,
176                Self::InformationEnabledRoles,
177            ],
178            P(VirtualRelation::InformationViews) => &[
179                P(PgNamespace),
180                P(PgClass),
181                P(PgTrigger),
182                P(PgTrigger),
183                P(PgTrigger),
184            ],
185            P(VirtualRelation::InformationRoutines) => &[
186                P(PgNamespace),
187                P(PgProc),
188                P(PgLanguage),
189                P(PgType),
190                P(PgNamespace),
191            ],
192            P(VirtualRelation::InformationSequences) => &[
193                P(PgNamespace),
194                P(PgClass),
195                Self::PgSequence,
196                P(VirtualRelation::PgDepend),
197            ],
198            P(VirtualRelation::InformationTableConstraints) => &[
199                P(PgNamespace),
200                P(PgNamespace),
201                P(PgConstraint),
202                P(PgClass),
203                P(PgIndex),
204            ],
205            P(VirtualRelation::InformationKeyColumnUsage) => &[
206                P(PgAttribute),
207                P(PgNamespace),
208                P(PgClass),
209                P(PgNamespace),
210                P(PgConstraint),
211            ],
212            P(VirtualRelation::PgRules) => &[P(PgRewrite), P(PgClass), P(PgNamespace)],
213            P(VirtualRelation::PgTables | VirtualRelation::PgMatviews) => {
214                &[P(PgClass), P(PgNamespace), Self::PgTablespace]
215            }
216            P(VirtualRelation::PgViews) => &[P(PgClass), P(PgNamespace)],
217            P(VirtualRelation::PgIndexes) => &[
218                P(PgIndex),
219                P(PgClass),
220                P(PgClass),
221                P(PgNamespace),
222                Self::PgTablespace,
223            ],
224            P(VirtualRelation::PgRoles) | Self::PgShadow => &[
225                Self::Projected(VirtualRelation::PgAuthid),
226                Self::PgDbRoleSetting,
227            ],
228            P(VirtualRelation::PgUser) => &[Self::PgShadow],
229            P(VirtualRelation::PgSequences) => &[Self::PgSequence, P(PgClass), P(PgNamespace)],
230            Self::InformationEnabledRoles => &[Self::Projected(VirtualRelation::PgAuthid)],
231            _ => &[],
232        }
233    }
234}
235
236#[cfg(test)]
237mod tests;