1use 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 #[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 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 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 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;