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 PgSequence => ("pg_catalog", "pg_sequence", 2224, "table"),
76 InformationEnabledRoles => ("information_schema", "enabled_roles", 13410, "view"),
77}
78
79impl SystemRelation {
80 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 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 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;