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_expression(
55        &self,
56        expression: &mut ExpressionPlan,
57        params: &[SQLParam],
58        outer: &RowSchema,
59    ) -> Result<Option<ColumnType>, SQLError> {
60        self.with_scope_result(|scope| {
61            crate::binding::bind_expression_plan_routines_for_storage(
62                self.routines,
63                expression,
64                params,
65                &scope.binding_context()?,
66                outer,
67            )
68        })
69    }
70}
71
72#[cfg(test)]
73mod tests;