Skip to main content

uqa_sql/binding/statements/
procedural.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Retained procedural input analysis and its selected catalog dependencies.
8
9use super::{AnalyzedResult, BindingContext, StatementAnalysisContext};
10use crate::{
11    catalog::resolution::EffectiveSearchPath,
12    plan::{CommandPlan, UnifiedPlan},
13    prepared::dependencies::{PreparedAnalysisDependencies, PreparedDependencySnapshot},
14    routines::{declaration::RoutineTypeCatalog, resolution::RoutineOverloadContext},
15    SQLError, SQLParam,
16};
17
18#[derive(Debug)]
19pub struct ProceduralPlanAnalysis {
20    pub result: AnalyzedResult,
21    pub dependencies: PreparedAnalysisDependencies,
22    pub effective_search_path: Option<EffectiveSearchPath>,
23    pub dependency_snapshot: Option<PreparedDependencySnapshot>,
24}
25
26/// Analyze a reached SQL occurrence once in its retained catalog scope. CALL
27/// resolves and converts its selected inputs before successful publication too.
28pub fn analyze_procedural_plan(
29    context: &StatementAnalysisContext<'_>,
30    overloads: &RoutineOverloadContext<'_>,
31    types: &dyn RoutineTypeCatalog,
32    plan: &mut UnifiedPlan,
33    params: &[SQLParam],
34) -> Result<ProceduralPlanAnalysis, SQLError> {
35    let mut analyzed = None;
36    context.scopes.with_scope(&mut |scope| {
37        let binding = scope.binding_context()?;
38        let (result, dependencies) =
39            analyze_inputs(context, overloads, types, plan, params, &binding)?;
40        crate::routines::compilation::bind_analyzed_sql_body_types(types, plan)?;
41        let effective_search_path = binding.catalog.effective_search_path(&binding.resolution)?;
42        let dependency_snapshot = binding
43            .catalog
44            .prepared_dependency_snapshot(&dependencies)?;
45        analyzed = Some(ProceduralPlanAnalysis {
46            result,
47            dependencies,
48            effective_search_path,
49            dependency_snapshot,
50        });
51        Ok(())
52    })?;
53    analyzed.ok_or_else(|| SQLError::Internal("procedural analysis scope did not run".into()))
54}
55
56fn analyze_inputs(
57    context: &StatementAnalysisContext<'_>,
58    overloads: &RoutineOverloadContext<'_>,
59    types: &dyn RoutineTypeCatalog,
60    plan: &mut UnifiedPlan,
61    params: &[SQLParam],
62    binding: &BindingContext<'_>,
63) -> Result<(AnalyzedResult, PreparedAnalysisDependencies), SQLError> {
64    if let UnifiedPlan::Command(command) = plan {
65        match command.as_mut() {
66            CommandPlan::Call { .. } => {
67                return crate::binding::preparation::prepare_procedure_call(
68                    context, overloads, types, plan, params, binding,
69                );
70            }
71            CommandPlan::Explain { body, .. } => {
72                let (_, dependencies) =
73                    analyze_inputs(context, overloads, types, body, params, binding)?;
74                return Ok((AnalyzedResult::Command, dependencies));
75            }
76            _ => {}
77        }
78    }
79    let dependencies = crate::binding::preparation::read_procedural_inputs(
80        context.routines,
81        plan,
82        params,
83        binding,
84        context.aliases,
85    )?;
86    let result = super::analyze_bound_result(context.routines, plan, params, binding)?;
87    Ok((result, dependencies))
88}