uqa_sql/binding/statements/
procedural.rs1use 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
26impl ProceduralPlanAnalysis {
27 pub fn prepared_definition(
29 &self,
30 plan: &UnifiedPlan,
31 params: &[SQLParam],
32 ) -> Result<crate::prepared::definition::PreparedDefinition, SQLError> {
33 let parameter_types = (1..=params.len())
34 .map(|index| {
35 crate::type_resolution::scalar_type(
36 &crate::ScalarExpr::Param(index),
37 &crate::RowSchema::new(Vec::new()),
38 params,
39 )
40 })
41 .collect::<Result<Vec<_>, _>>()?;
42 let result_schema = match &self.result {
43 AnalyzedResult::Command => None,
44 AnalyzedResult::Schema(schema) => Some(schema.clone()),
45 AnalyzedResult::Rows(types) => Some(crate::RowSchema::with_types(
46 vec![String::new(); types.len()],
47 types.clone(),
48 )),
49 };
50 Ok(crate::prepared::definition::PreparedDefinition {
51 logical_plan: plan.clone(),
52 parameter_types,
53 result_schema,
54 effective_search_path: self.effective_search_path.clone(),
55 dependencies: self.dependencies.clone(),
56 dependency_snapshot: self.dependency_snapshot.clone(),
57 })
58 }
59}
60
61pub fn analyze_procedural_plan(
64 context: &StatementAnalysisContext<'_>,
65 overloads: &RoutineOverloadContext<'_>,
66 types: &dyn RoutineTypeCatalog,
67 plan: &mut UnifiedPlan,
68 params: &[SQLParam],
69) -> Result<ProceduralPlanAnalysis, SQLError> {
70 let mut analyzed = None;
71 context.scopes.with_scope(&mut |scope| {
72 let binding = scope.binding_context()?;
73 let (result, dependencies) =
74 analyze_inputs(context, overloads, types, plan, params, &binding)?;
75 crate::routines::compilation::bind_analyzed_sql_body_types(types, plan)?;
76 let effective_search_path = binding.catalog.effective_search_path(&binding.resolution)?;
77 let dependency_snapshot = binding
78 .catalog
79 .prepared_dependency_snapshot(&dependencies)?;
80 analyzed = Some(ProceduralPlanAnalysis {
81 result,
82 dependencies,
83 effective_search_path,
84 dependency_snapshot,
85 });
86 Ok(())
87 })?;
88 analyzed.ok_or_else(|| SQLError::Internal("procedural analysis scope did not run".into()))
89}
90
91fn analyze_inputs(
92 context: &StatementAnalysisContext<'_>,
93 overloads: &RoutineOverloadContext<'_>,
94 types: &dyn RoutineTypeCatalog,
95 plan: &mut UnifiedPlan,
96 params: &[SQLParam],
97 binding: &BindingContext<'_>,
98) -> Result<(AnalyzedResult, PreparedAnalysisDependencies), SQLError> {
99 if let UnifiedPlan::Command(command) = plan {
100 match command.as_mut() {
101 CommandPlan::Call { .. } => {
102 return crate::binding::preparation::prepare_procedure_call(
103 context, overloads, types, plan, params, binding,
104 );
105 }
106 CommandPlan::Explain { body, .. } => {
107 let (_, dependencies) =
108 analyze_inputs(context, overloads, types, body, params, binding)?;
109 return Ok((AnalyzedResult::Command, dependencies));
110 }
111 _ => {}
112 }
113 }
114 let dependencies = crate::binding::preparation::read_procedural_inputs(
115 context.routines,
116 plan,
117 params,
118 binding,
119 context.aliases,
120 )?;
121 let result = super::analyze_bound_result(context.routines, plan, params, binding)?;
122 Ok((result, dependencies))
123}