uqa-sql 0.4.0

PostgreSQL-compatible SQL compiler built on libpg_query
Documentation
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//

use super::super::context::{RoleDependencyRead, RoleTableSecurity, RoleTablesRead};
use super::*;
use crate::catalog::security::BoundTableSecurity;
use crate::catalog::{roles::RoleDefinition, security::database::BoundDatabaseSecurity};
use crate::{catalog::stored_view::StoredView, routines::SQLUserFunction};
use std::{cell::RefCell, ops::Deref, rc::Rc, sync::Arc};

pub(super) type Events = Rc<RefCell<Vec<String>>>;
pub(super) struct Table {
    pub name: String,
    pub persistence: crate::ast::RelationPersistence,
    pub security: BoundTableSecurity,
    pub events: Events,
}
impl RoleTableSecurity for Table {
    fn persistence(&self) -> crate::ast::RelationPersistence {
        self.persistence
    }
    fn security(&self) -> BoundTableSecurity {
        self.events
            .borrow_mut()
            .push(format!("security {}", self.name));
        self.security.clone()
    }
}
struct Read<'a, T> {
    value: &'a T,
    name: &'static str,
    events: Events,
}
impl<T> Deref for Read<'_, T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.value
    }
}
impl<T> Drop for Read<'_, T> {
    fn drop(&mut self) {
        self.events
            .borrow_mut()
            .push(format!("release {}", self.name));
    }
}
impl RoleTablesRead for Read<'_, BTreeMap<RelationIdentity, Table>> {
    fn iter(&self) -> Box<dyn Iterator<Item = (&RelationIdentity, &dyn RoleTableSecurity)> + '_> {
        Box::new(
            self.value
                .iter()
                .map(|(relation, table)| (relation, table as &dyn RoleTableSecurity)),
        )
    }
}
pub(super) struct Catalog {
    pub roles: BTreeMap<String, RoleDefinition>,
    pub database: BoundDatabaseSecurity,
    pub schemas: BTreeMap<String, BoundSchemaSecurity>,
    pub tables: BTreeMap<RelationIdentity, Table>,
    pub views: BTreeMap<RelationIdentity, StoredView>,
    pub foreign_tables: BTreeMap<RelationIdentity, BoundTableSecurity>,
    pub system_relations: crate::catalog::security::system_relations::SystemRelationSecurities,
    pub sequences: BTreeMap<RelationIdentity, BoundSequenceSecurity>,
    pub sequence_persistence: BTreeMap<RelationIdentity, crate::ast::RelationPersistence>,
    pub routines: BTreeMap<String, Vec<Arc<SQLUserFunction>>>,
    pub domains: BTreeMap<String, crate::catalog::domain::StoredDomain>,
    pub events: Events,
}
impl Catalog {
    pub fn new() -> Self {
        Self {
            roles: ["uqa", "first", "second", "unreferenced"]
                .into_iter()
                .enumerate()
                .map(|(index, name)| {
                    let mut role = RoleDefinition::bootstrap();
                    if index != 0 {
                        role.name = name.into();
                        role.oid = 20_000 + index as i64;
                        role.object_id = [index as u8; 16];
                        role.attributes.clear();
                    }
                    (name.into(), role)
                })
                .collect(),
            database: BoundDatabaseSecurity::bootstrap(),
            schemas: BTreeMap::new(),
            tables: BTreeMap::new(),
            views: BTreeMap::new(),
            foreign_tables: BTreeMap::new(),
            system_relations: BTreeMap::new(),
            sequences: BTreeMap::new(),
            sequence_persistence: BTreeMap::new(),
            routines: BTreeMap::new(),
            domains: BTreeMap::new(),
            events: Rc::default(),
        }
    }
    fn read<'a, T>(&self, name: &'static str, value: &'a T) -> Read<'a, T> {
        self.events.borrow_mut().push(format!("read {name}"));
        Read {
            value,
            name,
            events: self.events.clone(),
        }
    }
    pub fn table(&mut self, name: &str, owner: crate::catalog::roles::RoleIdentity) {
        self.tables.insert(
            RelationIdentity::new("public", name),
            Table {
                name: name.into(),
                persistence: crate::ast::RelationPersistence::Permanent,
                security: BoundTableSecurity::owner(owner),
                events: self.events.clone(),
            },
        );
    }
}

impl super::super::context::TemporaryRoleDependencyCatalog for Catalog {
    fn temporary_namespace_allocated(&self) -> bool {
        true
    }
    fn sequence_persistence(
        &self,
    ) -> RoleDependencyRead<'_, BTreeMap<RelationIdentity, crate::ast::RelationPersistence>> {
        Box::new(self.read("sequence persistence", &self.sequence_persistence))
    }
}
impl RoleDependencyCatalog for Catalog {
    fn database(&self) -> RoleDependencyRead<'_, BoundDatabaseSecurity> {
        Box::new(self.read("database", &self.database))
    }
    fn schemas(&self) -> RoleDependencyRead<'_, BTreeMap<String, BoundSchemaSecurity>> {
        Box::new(self.read("schemas", &self.schemas))
    }
    fn tables(&self) -> Box<dyn RoleTablesRead + '_> {
        Box::new(self.read("tables", &self.tables))
    }
    fn views(&self) -> RoleDependencyRead<'_, BTreeMap<RelationIdentity, StoredView>> {
        Box::new(self.read("views", &self.views))
    }
    fn foreign_tables(
        &self,
    ) -> RoleDependencyRead<'_, BTreeMap<RelationIdentity, BoundTableSecurity>> {
        Box::new(self.read("foreign", &self.foreign_tables))
    }
    fn sequences(
        &self,
    ) -> RoleDependencyRead<'_, BTreeMap<RelationIdentity, BoundSequenceSecurity>> {
        Box::new(self.read("sequences", &self.sequences))
    }
    fn routines(&self) -> RoleDependencyRead<'_, BTreeMap<String, Vec<Arc<SQLUserFunction>>>> {
        Box::new(self.read("routines", &self.routines))
    }
    fn domains(
        &self,
    ) -> RoleDependencyRead<'_, BTreeMap<String, crate::catalog::domain::StoredDomain>> {
        Box::new(self.read("domains", &self.domains))
    }
}

impl crate::catalog::security::system_relations::SystemRelationSecurityCatalog for Catalog {
    fn system_relation_securities(
        &self,
    ) -> crate::catalog::security::system_relations::SystemRelationSecurityRead<'_> {
        Box::new(&self.system_relations)
    }
}