uqa-engine 0.5.1

Engine: schema-aware table store, catalog restore, transactions
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//

//! Bind routine definition analysis to current catalog data and compilation namespaces.

use crate::Engine;
use uqa_sql::{
    ast::{ColumnDef, ColumnType},
    binding::{snapshot::BindingSnapshot, stored_relations::StoredQueryNamespace},
    plpgsql::PlpgsqlCatalog,
    routines::{
        compilation::{RoutineCompilationCatalog, RoutineCompilationContext, RoutineParserCatalog},
        declaration::RoutineTypeCatalog,
        merge_columns::StoredMergeColumnCatalog,
        regclass::RoutineRegclassCatalog,
    },
    SQLError,
};

impl Engine {
    pub(crate) fn routine_compilation_context(&self) -> RoutineCompilationContext<'_> {
        RoutineCompilationContext {
            types: self,
            parsers: self,
            catalog: self,
            routines: self,
            relations: self,
            sequences: self,
            merge: self,
            regroles: self,
        }
    }
}
impl RoutineTypeCatalog for Engine {
    fn try_describe_table(&self, name: &str) -> Result<Option<Vec<ColumnDef>>, String> {
        Engine::describe_table_in_execution(self, name).map_err(|error| error.to_string())
    }
    fn resolve_catalog_column_type(&self, name: &str) -> Option<ColumnType> {
        uqa_execution::catalog::projection::resolve_catalog_column_type(
            &self.catalog_execution(),
            name,
        )
    }
    fn resolve_catalog_column_type_name(&self, name: &str) -> Result<ColumnType, SQLError> {
        uqa_execution::catalog::projection::resolve_catalog_column_type_name(
            &self.catalog_execution(),
            name,
        )
    }
    fn resolve_catalog_user_type_by_oid(&self, oid: u32) -> Option<ColumnType> {
        uqa_execution::catalog::projection::resolve_catalog_user_type_by_oid(
            &self.catalog_execution(),
            oid,
        )
    }
    fn format_type(&self, ty: &ColumnType) -> Result<String, SQLError> {
        uqa_execution::catalog::projection::format_type_name(&self.catalog_execution(), ty)
    }
    fn format_type_oid(&self, oid: u32) -> Result<String, SQLError> {
        uqa_execution::catalog::projection::format_type_object(
            &self.catalog_execution(),
            i64::from(oid),
        )
        .map_err(SQLError::Internal)?
        .ok_or_else(|| SQLError::Internal(format!("cache lookup failed for type {oid}")))
    }
    fn composite_types(&self) -> Option<&dyn uqa_sql::expr::composites::CompositeTypeCatalog> {
        Some(self)
    }
    fn require_type_usage(&self, ty: &ColumnType) -> Result<(), SQLError> {
        uqa_execution::catalog::security::type_inquiry::require_type_usage(
            &self.catalog_execution(),
            ty,
        )
    }
}
impl uqa_execution::routines::invocation::bodies::RoutineBodySession for Engine {
    fn retain_routine_body(
        &self,
        function: &uqa_sql::routines::SQLUserFunction,
        body: uqa_sql::routines::CompiledFunctionBody,
    ) -> Result<(), SQLError> {
        self.session.routine_bodies.retain(function, body)
    }
}
impl StoredMergeColumnCatalog for Engine {
    fn stored_merge_target_definitions(&self, table: &str) -> Option<Vec<ColumnDef>> {
        self.table_entries()
            .into_iter()
            .find(|(name, _)| name == table)
            .map(|(_, table)| table.columns.read().clone())
    }
}
impl RoutineParserCatalog for Engine {
    fn parser_settings(&self) -> uqa_sql::parser::ParserSettings {
        Engine::parser_settings(self)
    }

    fn parser_notice(&self, notice: uqa_sql::SQLNotice) {
        self.runtime.notices.push(notice);
    }

    fn plpgsql_catalog(&self) -> Result<PlpgsqlCatalog, SQLError> {
        uqa_execution::catalog::projection::plpgsql_catalog(&self.catalog_execution())
    }
}
impl RoutineCompilationCatalog for Engine {
    fn has_registered_aggregate_function(&self, name: &str) -> bool {
        Engine::has_registered_aggregate_function(self, name)
    }
    fn binding_snapshot(&self) -> Result<BindingSnapshot, SQLError> {
        let scope = super::query_scope::new_for_catalog_binding(self);
        uqa_execution::query::binding::binding_context(&scope).map(Into::into)
    }
    fn stored_query_namespace(&self) -> StoredQueryNamespace {
        StoredQueryNamespace {
            temporary_schema: self.temporary_schema_name(),
            transition_relations:
                uqa_execution::mutation::triggers::current_transition_relation_names(),
        }
    }
}
impl RoutineRegclassCatalog for Engine {
    fn resolve_routine_regclass(&self, reference: &str) -> Result<Option<i64>, SQLError> {
        uqa_execution::catalog::projection::resolve_regclass_oid(
            &self.catalog_execution(),
            reference,
        )
    }
}

use uqa_core::RelationIdentity;
use uqa_execution::routines::{
    compilation::{RoutineCompilationSession, StoredRoutineCompilationContext},
    definition::RoutineDefinitionContext,
    rewrites::{self, RoutineRewriteContext},
};

impl Engine {
    pub(crate) fn routine_definition_context(&self) -> RoutineDefinitionContext<'_> {
        RoutineDefinitionContext {
            compilation: self.stored_routine_compilation_context(),
            sources: self,
            regclasses: self,
        }
    }
    pub(crate) fn stored_routine_compilation_context(&self) -> StoredRoutineCompilationContext<'_> {
        StoredRoutineCompilationContext {
            analysis: self.routine_compilation_context(),
            session: self,
        }
    }
    pub(crate) fn routine_rewrite_context(&self) -> RoutineRewriteContext<'_> {
        RoutineRewriteContext {
            registry: self,
            publication: self,
            compilation: self.stored_routine_compilation_context(),
            columns: self.stored_column_binding_context(),
            changes: self,
        }
    }
    pub(crate) fn rewrite_routine_relation_references(
        &self,
        from: &RelationIdentity,
        to: &RelationIdentity,
    ) -> Result<(), SQLError> {
        rewrites::rewrite_routine_relation_references(&self.routine_rewrite_context(), from, to)
    }
    pub(crate) fn rewrite_routine_column_references(
        &self,
        relation: &RelationIdentity,
        from: &str,
        to: &str,
    ) -> Result<(), SQLError> {
        rewrites::rewrite_routine_column_references(
            &self.routine_rewrite_context(),
            relation,
            from,
            to,
        )
    }
}
impl RoutineCompilationSession for Engine {
    fn routine_search_path(&self) -> Vec<String> {
        crate::session::effective_search_path(&self.session.state.read())
    }
    fn replace_routine_search_path(&self, path: Vec<String>) -> Vec<String> {
        std::mem::replace(&mut self.session.state.write().search_path, path)
    }
    fn restore_routine_search_path(&self, path: Vec<String>) {
        self.session.state.write().search_path = path;
    }
    fn routine_settings_scope(
        &self,
        settings: &[(String, String)],
    ) -> Result<Box<dyn RoutineConfigurationGuard + '_>, SQLError> {
        let scope = self.routine_invocation_state_guard(!settings.is_empty(), false);
        for (name, value) in settings {
            self.set_configured_parameter(name, value)?;
        }
        Ok(Box::new(scope))
    }
}

use uqa_execution::routines::{
    catalog::RoutineMutationContext,
    configuration::{RoutineConfigurationGuard, RoutineConfigurationSession},
    registration::RoutineRegistrationContext,
};
use uqa_sql::routines::registration::RoutineSupportAuthority;

impl RoutineSupportAuthority for Engine {
    fn current_user_is_superuser(&self) -> bool {
        Engine::current_user_is_superuser(self)
    }
}
impl RoutineConfigurationGuard for crate::roles::RoutineSessionStateGuard<'_> {}
impl RoutineConfigurationSession for Engine {
    fn routine_configuration_guard(&self) -> Box<dyn RoutineConfigurationGuard + '_> {
        Box::new(self.routine_config_state_guard())
    }
    fn set_routine_variable(&self, name: &str, value: &str) -> Result<(), SQLError> {
        self.set_variable(name, value)
    }
    fn show_routine_variable(&self, name: &str) -> Result<String, SQLError> {
        self.show_variable(name)
    }
    fn routine_variable_name(&self, name: &str) -> Result<String, SQLError> {
        self.session_execution_view()
            .show_parameter(name)
            .map(|(name, _)| name)
    }
    fn load_language_library(&self, language: &str) {
        self.load_language(language);
    }
}
impl Engine {
    pub(crate) fn routine_mutation_context(&self) -> RoutineMutationContext<'_> {
        RoutineMutationContext {
            writer: self,
            names: self,
            roles: self,
            registry: self,
            publication: self,
            changes: self,
        }
    }
    pub(crate) fn routine_registration_context(&self) -> RoutineRegistrationContext<'_> {
        RoutineRegistrationContext {
            catalog: self.routine_mutation_context(),
            namespace: self.relation_creation_context(),
            definition: self.routine_definition_context(),
            support: self,
            configuration: self,
            overloads: uqa_sql::routines::resolution::RoutineOverloadContext { catalog: self },
            session: self,
            bodies: self,
        }
    }
}