uqa_sql/semantics/
table_locks.rs1use crate::catalog::roles::RoleReference;
10use crate::{
11 ast::{LockTableTarget, TableLockMode},
12 catalog::{
13 roles::guards::RoleCatalogGuards,
14 security::{
15 table::{role_has_table_privilege, TableAclPrivilege},
16 BoundTableSecurity,
17 },
18 },
19 plan::QueryPlan,
20 SQLError,
21};
22
23pub fn ensure_lock_privilege(
24 roles: &dyn RoleCatalogGuards,
25 security: &BoundTableSecurity,
26 subject: &RoleReference,
27 mode: TableLockMode,
28 name: &str,
29 kind: &str,
30) -> Result<(), SQLError> {
31 let definitions = roles.role_definitions();
32 let memberships = roles.role_memberships();
33 let security = security.resolve(&definitions).map_err(SQLError::Internal)?;
34 let system = crate::catalog::SystemRelation::from_qualified_name(name);
35 if TableAclPrivilege::ALL.into_iter().any(|privilege| {
36 lock_privilege_permits(privilege, mode)
37 && system.map_or_else(
38 || {
39 role_has_table_privilege(
40 &security,
41 subject,
42 privilege,
43 &definitions,
44 &memberships,
45 )
46 },
47 |relation| {
48 crate::catalog::security::system_relations::has_table_privilege(
49 relation,
50 &security,
51 subject,
52 crate::catalog::security::table::TablePrivilegeCheck {
53 privilege,
54 grant_option: false,
55 },
56 &definitions,
57 &memberships,
58 )
59 },
60 )
61 }) {
62 return Ok(());
63 }
64 let (_, local) =
65 uqa_core::RelationIdentity::parse_reference(name).map_err(SQLError::Internal)?;
66 Err(SQLError::Routine {
67 sqlstate: "42501".into(),
68 message: format!("permission denied for {kind} {local}"),
69 })
70}
71
72pub fn lock_privilege_permits(privilege: TableAclPrivilege, mode: TableLockMode) -> bool {
73 match privilege {
74 TableAclPrivilege::Maintain
75 | TableAclPrivilege::Update
76 | TableAclPrivilege::Delete
77 | TableAclPrivilege::Truncate => true,
78 TableAclPrivilege::Select => mode == TableLockMode::AccessShare,
79 TableAclPrivilege::Insert => matches!(
80 mode,
81 TableLockMode::AccessShare | TableLockMode::RowShare | TableLockMode::RowExclusive
82 ),
83 TableAclPrivilege::References | TableAclPrivilege::Trigger => false,
84 }
85}
86
87pub fn view_lock_targets(query: &QueryPlan) -> Result<Vec<LockTableTarget>, SQLError> {
88 let mut targets = Vec::new();
89 crate::binding::view_dependencies::bind_query_plan_relation_targets(
90 &mut query.clone(),
91 &std::collections::BTreeSet::new(),
92 &mut |name, include_descendants| {
93 targets.push(LockTableTarget {
94 name: name.into(),
95 include_descendants,
96 });
97 Ok::<_, SQLError>(name.into())
98 },
99 )?;
100 Ok(targets)
101}
102
103#[cfg(test)]
104mod tests;