Skip to main content

uqa_sql/prepared/
definition.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Analyze prepared declarations with separate retained inference and descriptor scopes.
8
9use crate::{
10    binding::statements::{StatementAnalysisScopes, StatementBindingScope},
11    plan::UnifiedPlan,
12    routines::RoutineResolution,
13    ColumnType, FunctionTypeResolver, RowSchema, SQLError,
14};
15
16#[derive(Clone, Copy)]
17pub struct PreparedDefinitionContext<'a> {
18    pub types: &'a dyn FunctionTypeResolver,
19    pub routines: &'a dyn RoutineResolution,
20    pub scopes: &'a dyn StatementAnalysisScopes,
21}
22
23pub struct PreparedDefinition {
24    pub logical_plan: UnifiedPlan,
25    pub parameter_types: Vec<Option<ColumnType>>,
26    pub result_schema: Option<RowSchema>,
27}
28
29pub fn analyze_definition(
30    context: &PreparedDefinitionContext<'_>,
31    mut logical_plan: UnifiedPlan,
32    declared: &[ColumnType],
33) -> Result<PreparedDefinition, SQLError> {
34    let parameter_types =
35        super::declared_parameter_types(context.types, &mut logical_plan, declared)?;
36    let parameter_types = with_scope_result(context.scopes, |scope| {
37        crate::binding::infer_prepared_parameter_types(
38            context.routines,
39            &logical_plan,
40            &parameter_types,
41            &scope.binding_context()?,
42        )
43    })?;
44    let result_schema = analyze_result_schema(context, &logical_plan, &parameter_types)?;
45    Ok(PreparedDefinition {
46        logical_plan,
47        parameter_types,
48        result_schema,
49    })
50}
51
52pub fn analyze_result_schema(
53    context: &PreparedDefinitionContext<'_>,
54    logical_plan: &UnifiedPlan,
55    parameter_types: &[Option<ColumnType>],
56) -> Result<Option<RowSchema>, SQLError> {
57    with_scope_result(context.scopes, |scope| {
58        super::analyze_prepared_plan(
59            context.routines,
60            logical_plan,
61            parameter_types,
62            &scope.binding_context()?,
63        )
64    })
65}
66
67fn with_scope_result<T>(
68    scopes: &dyn StatementAnalysisScopes,
69    mut analyze: impl FnMut(&dyn StatementBindingScope) -> Result<T, SQLError>,
70) -> Result<T, SQLError> {
71    let mut result = None;
72    scopes.with_scope(&mut |scope| {
73        result = Some(analyze(scope)?);
74        Ok(())
75    })?;
76    result.ok_or_else(|| {
77        SQLError::Internal("prepared analysis scope did not invoke its operation".into())
78    })
79}
80
81#[cfg(test)]
82mod tests;