uqa_sql/catalog/roles/dependencies/
temporary.rs1use super::context::TemporaryRoleDependencyCatalog;
10use crate::{
11 ast::RelationPersistence,
12 catalog::{
13 roles::RoleDefinition,
14 security::{dependencies::AclRoleReferences, BoundTableSecurity},
15 },
16 SQLError,
17};
18use std::collections::{BTreeMap, BTreeSet};
19
20pub fn role_dependencies(
21 catalog: &dyn TemporaryRoleDependencyCatalog,
22 roles: &BTreeMap<String, RoleDefinition>,
23 limit: usize,
24) -> Result<BTreeSet<u32>, SQLError> {
25 let mut referenced = BTreeSet::new();
26 {
27 let tables = catalog.tables();
28 for (_, table) in tables.iter() {
29 if table.persistence() == RelationPersistence::Temporary {
30 table_dependencies(&table.security(), roles, limit, &mut referenced)?;
31 }
32 }
33 }
34 {
35 let views = catalog.views();
36 for view in views.values() {
37 if view.persistence == RelationPersistence::Temporary {
38 table_dependencies(&view.security(), roles, limit, &mut referenced)?;
39 }
40 }
41 }
42 {
43 let persistence = catalog.foreign_table_persistence();
44 let security = catalog.foreign_tables();
45 for (relation, persistence) in persistence.iter() {
46 if persistence == RelationPersistence::Temporary {
47 let security = security.get(relation).ok_or_else(|| {
48 SQLError::Internal(format!(
49 "temporary foreign table `{}` has no security metadata",
50 relation.qualified_name()
51 ))
52 })?;
53 table_dependencies(security, roles, limit, &mut referenced)?;
54 }
55 }
56 }
57 {
58 let sequences = catalog.sequences();
59 let persistence = catalog.sequence_persistence();
60 for (name, security) in sequences.iter() {
61 if persistence.get(name) == Some(&RelationPersistence::Temporary) {
62 let security = security.resolve(roles).map_err(SQLError::Internal)?;
63 add_role(&security.role_owner, roles, limit, &mut referenced)?;
64 acl_dependencies(
65 security.acl.as_deref().unwrap_or_default(),
66 roles,
67 limit,
68 &mut referenced,
69 )?;
70 }
71 }
72 }
73 Ok(referenced)
74}
75
76fn table_dependencies(
77 security: &BoundTableSecurity,
78 roles: &BTreeMap<String, RoleDefinition>,
79 limit: usize,
80 referenced: &mut BTreeSet<u32>,
81) -> Result<(), SQLError> {
82 let named = security.resolve(roles).map_err(SQLError::Internal)?;
84 add_role(&named.role_owner, roles, limit, referenced)?;
85 acl_dependencies(
86 named.acl.as_deref().unwrap_or_default(),
87 roles,
88 limit,
89 referenced,
90 )?;
91 for acl in named.column_acls.values() {
92 acl_dependencies(acl, roles, limit, referenced)?;
93 }
94 Ok(())
95}
96
97fn acl_dependencies<T: AclRoleReferences>(
98 acl: &[T],
99 roles: &BTreeMap<String, RoleDefinition>,
100 limit: usize,
101 referenced: &mut BTreeSet<u32>,
102) -> Result<(), SQLError> {
103 for entry in acl {
104 let (grantee, grantor) = entry.role_references();
105 if let Some(grantee) = grantee {
106 add_role(grantee, roles, limit, referenced)?;
107 }
108 if let Some(grantor) = grantor {
109 add_role(grantor, roles, limit, referenced)?;
110 }
111 }
112 Ok(())
113}
114
115fn add_role(
116 name: &str,
117 roles: &BTreeMap<String, RoleDefinition>,
118 limit: usize,
119 referenced: &mut BTreeSet<u32>,
120) -> Result<(), SQLError> {
121 let role = roles.get(name).ok_or_else(|| SQLError::Routine {
122 sqlstate: "42704".into(),
123 message: format!("role \"{name}\" does not exist"),
124 })?;
125 if role.oid <= 0 {
126 return Err(SQLError::Internal("invalid role OID".into()));
127 }
128 if role.oid != 10 {
129 referenced.insert(
130 u32::try_from(role.oid).map_err(|_| SQLError::Internal("invalid role OID".into()))?,
131 );
132 if referenced.len() > limit {
133 return Err(SQLError::Routine {
134 sqlstate: "53200".into(),
135 message: "temporary role dependency capacity exhausted".into(),
136 });
137 }
138 }
139 Ok(())
140}