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