use super::{AnalyzedResult, BindingContext, StatementAnalysisContext};
use crate::{
catalog::resolution::EffectiveSearchPath,
plan::{CommandPlan, UnifiedPlan},
prepared::dependencies::{PreparedAnalysisDependencies, PreparedDependencySnapshot},
routines::{declaration::RoutineTypeCatalog, resolution::RoutineOverloadContext},
SQLError, SQLParam,
};
#[derive(Debug)]
pub struct ProceduralPlanAnalysis {
pub result: AnalyzedResult,
pub dependencies: PreparedAnalysisDependencies,
pub effective_search_path: Option<EffectiveSearchPath>,
pub dependency_snapshot: Option<PreparedDependencySnapshot>,
}
impl ProceduralPlanAnalysis {
pub fn prepared_definition(
&self,
plan: &UnifiedPlan,
params: &[SQLParam],
) -> Result<crate::prepared::definition::PreparedDefinition, SQLError> {
let parameter_types = (1..=params.len())
.map(|index| {
crate::type_resolution::scalar_type(
&crate::ScalarExpr::Param(index),
&crate::RowSchema::new(Vec::new()),
params,
)
})
.collect::<Result<Vec<_>, _>>()?;
let result_schema = match &self.result {
AnalyzedResult::Command => None,
AnalyzedResult::Schema(schema) => Some(schema.clone()),
AnalyzedResult::Rows(types) => Some(crate::RowSchema::with_types(
vec![String::new(); types.len()],
types.clone(),
)),
};
Ok(crate::prepared::definition::PreparedDefinition {
logical_plan: plan.clone(),
parameter_types,
result_schema,
effective_search_path: self.effective_search_path.clone(),
dependencies: self.dependencies.clone(),
dependency_snapshot: self.dependency_snapshot.clone(),
})
}
}
pub fn analyze_procedural_plan(
context: &StatementAnalysisContext<'_>,
overloads: &RoutineOverloadContext<'_>,
types: &dyn RoutineTypeCatalog,
plan: &mut UnifiedPlan,
params: &[SQLParam],
) -> Result<ProceduralPlanAnalysis, SQLError> {
let mut analyzed = None;
context.scopes.with_scope(&mut |scope| {
let binding = scope.binding_context()?;
let (result, dependencies) =
analyze_inputs(context, overloads, types, plan, params, &binding)?;
crate::routines::compilation::bind_analyzed_sql_body_types(types, plan)?;
let effective_search_path = binding.catalog.effective_search_path(&binding.resolution)?;
let dependency_snapshot = binding
.catalog
.prepared_dependency_snapshot(&dependencies)?;
analyzed = Some(ProceduralPlanAnalysis {
result,
dependencies,
effective_search_path,
dependency_snapshot,
});
Ok(())
})?;
analyzed.ok_or_else(|| SQLError::Internal("procedural analysis scope did not run".into()))
}
fn analyze_inputs(
context: &StatementAnalysisContext<'_>,
overloads: &RoutineOverloadContext<'_>,
types: &dyn RoutineTypeCatalog,
plan: &mut UnifiedPlan,
params: &[SQLParam],
binding: &BindingContext<'_>,
) -> Result<(AnalyzedResult, PreparedAnalysisDependencies), SQLError> {
if let UnifiedPlan::Command(command) = plan {
match command.as_mut() {
CommandPlan::Call { .. } => {
return crate::binding::preparation::prepare_procedure_call(
context, overloads, types, plan, params, binding,
);
}
CommandPlan::Explain { body, .. } => {
let (_, dependencies) =
analyze_inputs(context, overloads, types, body, params, binding)?;
return Ok((AnalyzedResult::Command, dependencies));
}
_ => {}
}
}
let dependencies = crate::binding::preparation::read_procedural_inputs(
context.routines,
plan,
params,
binding,
context.aliases,
)?;
let result = super::analyze_bound_result(context.routines, plan, params, binding)?;
Ok((result, dependencies))
}