Skip to main content

uqa_sql/binding/stored_routines/
analysis.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Retain a fresh catalog namespace while binding stored statement and expression routines.
8
9use super::{BoundStatementRoutines, CatalogRoutineContext};
10use crate::{
11    ast::ColumnType,
12    binding::statements::{StatementAnalysisOperation, StatementBindingScope},
13    plan::{ExpressionPlan, UnifiedPlan},
14    routines::RoutineResolution,
15    RowSchema, SQLError, SQLParam,
16};
17
18/// Catalog binding uses restored relation metadata and bound names without a current-routine row overlay.
19pub trait CatalogRoutineScopes {
20    fn with_catalog_scope(&self, analyze: StatementAnalysisOperation<'_>) -> Result<(), SQLError>;
21}
22#[derive(Clone, Copy)]
23pub struct CatalogRoutineAnalysisContext<'a> {
24    pub scopes: &'a dyn CatalogRoutineScopes,
25    pub routines: &'a dyn RoutineResolution,
26}
27impl CatalogRoutineAnalysisContext<'_> {
28    fn with_scope_result<T>(
29        &self,
30        mut analyze: impl FnMut(&dyn StatementBindingScope) -> Result<T, SQLError>,
31    ) -> Result<T, SQLError> {
32        let mut result = None;
33        self.scopes.with_catalog_scope(&mut |scope| {
34            result = Some(analyze(scope)?);
35            Ok(())
36        })?;
37        result.ok_or_else(|| {
38            SQLError::Internal("catalog routine scope did not invoke analysis".into())
39        })
40    }
41    pub fn bind_statement(&self, plan: &UnifiedPlan) -> Result<BoundStatementRoutines, SQLError> {
42        self.with_scope_result(|scope| {
43            let binding = scope.binding_context()?;
44            super::bind_catalog_statement_routines(
45                &CatalogRoutineContext {
46                    routines: self.routines,
47                    binding: &binding,
48                },
49                plan,
50                &[],
51            )
52        })
53    }
54    pub fn bind_rule_inputs(
55        &self,
56        expression: &mut ExpressionPlan,
57        binding: &crate::catalog::events::RuleConditionBinding,
58        table: &str,
59        columns: &[(String, ColumnType)],
60    ) -> Result<(), SQLError> {
61        let schema = binding.row_schema(columns);
62        self.with_scope_result(|scope| {
63            crate::binding::rule_inputs::expand(
64                self.routines,
65                &scope.binding_context()?,
66                expression,
67                binding,
68                table,
69                &schema,
70            )
71        })
72    }
73    pub fn bind_expression(
74        &self,
75        expression: &mut ExpressionPlan,
76        params: &[SQLParam],
77        outer: &RowSchema,
78    ) -> Result<Option<ColumnType>, SQLError> {
79        self.with_scope_result(|scope| {
80            crate::binding::bind_expression_plan_routines_for_storage(
81                self.routines,
82                expression,
83                params,
84                &scope.binding_context()?,
85                outer,
86            )
87        })
88    }
89}
90
91#[cfg(test)]
92mod tests;