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, PgIndex, PgLanguage,
140            PgNamespace, PgProc, PgRewrite, PgTrigger, PgType,
141        };
142        if let Some(sources) = self.constraint_view_sources() {
143            return sources;
144        }
145        match self {
146            P(VirtualRelation::InformationSchemata) => {
147                &[P(PgNamespace), Self::Projected(VirtualRelation::PgAuthid)]
148            }
149            P(VirtualRelation::InformationTables) => {
150                &[P(PgNamespace), P(PgClass), P(PgType), P(PgNamespace)]
151            }
152            P(VirtualRelation::InformationColumns) => &[
153                P(PgAttribute),
154                P(PgAttrdef),
155                P(PgClass),
156                P(PgNamespace),
157                P(PgType),
158                P(PgNamespace),
159                P(PgType),
160                P(PgNamespace),
161                Self::PgCollation,
162                P(PgNamespace),
163                P(VirtualRelation::PgDepend),
164                Self::PgSequence,
165            ],
166            P(InformationColumnPrivileges) => &[
167                P(PgNamespace),
168                Self::Projected(VirtualRelation::PgAuthid),
169                P(PgAttribute),
170                P(PgClass),
171                P(PgClass),
172                P(PgAttribute),
173                P(PgClass),
174                Self::Projected(VirtualRelation::PgAuthid),
175            ],
176            P(VirtualRelation::InformationRoleColumnGrants) => &[
177                P(InformationColumnPrivileges),
178                Self::InformationEnabledRoles,
179                Self::InformationEnabledRoles,
180            ],
181            P(VirtualRelation::InformationViews) => &[
182                P(PgNamespace),
183                P(PgClass),
184                P(PgTrigger),
185                P(PgTrigger),
186                P(PgTrigger),
187            ],
188            P(VirtualRelation::InformationRoutines) => &[
189                P(PgNamespace),
190                P(PgProc),
191                P(PgLanguage),
192                P(PgType),
193                P(PgNamespace),
194            ],
195            P(VirtualRelation::InformationSequences) => &[
196                P(PgNamespace),
197                P(PgClass),
198                Self::PgSequence,
199                P(VirtualRelation::PgDepend),
200            ],
201            P(VirtualRelation::PgRules) => &[P(PgRewrite), P(PgClass), P(PgNamespace)],
202            P(VirtualRelation::PgTables | VirtualRelation::PgMatviews) => {
203                &[P(PgClass), P(PgNamespace), Self::PgTablespace]
204            }
205            P(VirtualRelation::PgViews) => &[P(PgClass), P(PgNamespace)],
206            P(VirtualRelation::PgIndexes) => &[
207                P(PgIndex),
208                P(PgClass),
209                P(PgClass),
210                P(PgNamespace),
211                Self::PgTablespace,
212            ],
213            P(VirtualRelation::PgRoles) | Self::PgShadow => &[
214                Self::Projected(VirtualRelation::PgAuthid),
215                Self::PgDbRoleSetting,
216            ],
217            P(VirtualRelation::PgUser) => &[Self::PgShadow],
218            P(VirtualRelation::PgSequences) => &[Self::PgSequence, P(PgClass), P(PgNamespace)],
219            Self::InformationEnabledRoles => &[Self::Projected(VirtualRelation::PgAuthid)],
220            _ => &[],
221        }
222    }
223    const fn constraint_view_sources(self) -> Option<&'static [Self]> {
224        use SystemRelation::Projected as P;
225        use VirtualRelation::{
226            PgAttribute, PgClass, PgConstraint, PgDepend, PgIndex, PgNamespace, PgType,
227        };
228        match self {
229            P(VirtualRelation::InformationTableConstraints) => Some(&[
230                P(PgNamespace),
231                P(PgNamespace),
232                P(PgConstraint),
233                P(PgClass),
234                P(PgIndex),
235            ]),
236            P(VirtualRelation::InformationCheckConstraints) => Some(&[
237                P(PgConstraint),
238                P(PgNamespace),
239                P(PgClass),
240                P(PgType),
241                P(PgConstraint),
242                P(PgNamespace),
243                P(PgClass),
244                P(PgType),
245                P(PgAttribute),
246            ]),
247            P(VirtualRelation::InformationDomainConstraints) => {
248                Some(&[P(PgNamespace), P(PgNamespace), P(PgConstraint), P(PgType)])
249            }
250            P(VirtualRelation::InformationKeyColumnUsage) => Some(&[
251                P(PgAttribute),
252                P(PgNamespace),
253                P(PgClass),
254                P(PgNamespace),
255                P(PgConstraint),
256            ]),
257            P(VirtualRelation::InformationConstraintColumnUsage) => Some(&[
258                P(PgNamespace),
259                P(PgClass),
260                P(PgAttribute),
261                P(PgDepend),
262                P(PgNamespace),
263                P(PgConstraint),
264                P(PgNamespace),
265                P(PgClass),
266                P(PgAttribute),
267                P(PgNamespace),
268                P(PgConstraint),
269                P(PgNamespace),
270                P(PgClass),
271                P(PgAttribute),
272                P(PgNamespace),
273                P(PgConstraint),
274            ]),
275            P(VirtualRelation::InformationConstraintTableUsage) => {
276                Some(&[P(PgConstraint), P(PgNamespace), P(PgClass), P(PgNamespace)])
277            }
278            _ => None,
279        }
280    }
281}
282
283#[cfg(test)]
284mod tests;