Skip to main content

uqa_sql/semantics/privileges/
context.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Immutable relation authorization and CTE name inputs for column lineage analysis.
8
9use crate::catalog::roles::RoleReference;
10use crate::{
11    catalog::resolution::RelationNameResolution,
12    plan::{CtePlan, QueryPlan},
13    SQLError,
14};
15use std::collections::BTreeMap;
16
17#[derive(Clone, Copy)]
18pub enum PrivilegeRelationKind {
19    System(crate::catalog::SystemRelation),
20    Table,
21    View,
22    MaterializedView,
23    ForeignTable,
24}
25impl PrivilegeRelationKind {
26    pub const fn has_system_columns(self) -> bool {
27        match self {
28            Self::System(relation) => matches!(relation.kind().as_bytes(), b"table"),
29            _ => matches!(self, Self::Table | Self::ForeignTable),
30        }
31    }
32    pub const fn description(self) -> &'static str {
33        match self {
34            Self::System(relation) => relation.kind(),
35            Self::Table => "table",
36            Self::View => "view",
37            Self::MaterializedView => "materialized view",
38            Self::ForeignTable => "foreign table",
39        }
40    }
41}
42pub struct PrivilegeRelation {
43    pub canonical: String,
44    pub columns: Vec<String>,
45    pub kind: PrivilegeRelationKind,
46}
47pub trait PrivilegeCatalog {
48    fn relation(
49        &self,
50        resolution: &RelationNameResolution,
51        name: &str,
52    ) -> Result<Option<PrivilegeRelation>, SQLError>;
53    fn has_select_privilege(
54        &self,
55        resolution: &RelationNameResolution,
56        relation: &PrivilegeRelation,
57        column: Option<&str>,
58        subject: &RoleReference,
59    ) -> Result<bool, SQLError>;
60}
61pub trait PrivilegeCteCatalog {
62    fn is_visible_cte(&self, name: &str) -> bool;
63    fn materialized_columns(&self, name: &str) -> Option<Vec<String>>;
64    fn deferred_reference(&self, name: &str) -> Option<&CtePlan>;
65    fn privilege_subject(&self) -> Result<&RoleReference, SQLError>;
66}
67#[derive(Clone)]
68pub struct PrivilegeScope<'a> {
69    pub catalog: &'a dyn PrivilegeCatalog,
70    pub resolution: RelationNameResolution,
71    pub inherited: &'a dyn PrivilegeCteCatalog,
72    pub scalar_subqueries: Vec<QueryPlan>,
73    local_ctes: BTreeMap<String, CtePlan>,
74}
75impl<'a> PrivilegeScope<'a> {
76    pub fn new(
77        catalog: &'a dyn PrivilegeCatalog,
78        resolution: RelationNameResolution,
79        inherited: &'a dyn PrivilegeCteCatalog,
80        scalar_subqueries: Vec<QueryPlan>,
81    ) -> Self {
82        Self {
83            catalog,
84            resolution,
85            inherited,
86            scalar_subqueries,
87            local_ctes: BTreeMap::new(),
88        }
89    }
90    pub fn insert_deferred(&mut self, plan: CtePlan) {
91        self.local_ctes.insert(plan.name.clone(), plan);
92    }
93    pub fn is_visible_cte(&self, name: &str) -> bool {
94        crate::semantics::cte_reference_name(name)
95            .is_some_and(|name| self.local_ctes.contains_key(&name))
96            || self.inherited.is_visible_cte(name)
97    }
98    pub fn materialized_for_scan(&self, name: &str) -> Option<Vec<String>> {
99        let canonical = crate::semantics::cte_reference_name(name)?;
100        if self.local_ctes.contains_key(&canonical) {
101            None
102        } else {
103            self.inherited.materialized_columns(name)
104        }
105    }
106    pub fn deferred_reference(&self, name: &str) -> Option<&CtePlan> {
107        self.local_ctes
108            .get(&crate::semantics::cte_reference_name(name)?)
109            .or_else(|| self.inherited.deferred_reference(name))
110    }
111
112    pub fn privilege_subject(&self) -> Result<&RoleReference, SQLError> {
113        self.inherited.privilege_subject()
114    }
115}