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