uqa_sql/prepared/
definition.rs1use super::dependencies::{PreparedAnalysisDependencies, PreparedDependencySnapshot};
10use crate::{
11 binding::statements::{StatementAnalysisScopes, StatementBindingScope},
12 catalog::resolution::EffectiveSearchPath,
13 plan::UnifiedPlan,
14 routines::RoutineResolution,
15 ColumnType, FunctionTypeResolver, RowSchema, SQLError,
16};
17
18#[derive(Clone, Copy)]
19pub struct PreparedDefinitionContext<'a> {
20 pub types: &'a dyn FunctionTypeResolver,
21 pub routines: &'a dyn RoutineResolution,
22 pub scopes: &'a dyn StatementAnalysisScopes,
23 pub aliases: &'a dyn crate::schema::dependencies::oid_alias::OidAliasInput,
25}
26
27pub struct PreparedDefinition {
28 pub composite_inputs: super::composites::CompositeInputs,
29 pub logical_plan: UnifiedPlan,
30 pub parameter_types: Vec<Option<ColumnType>>,
31 pub result_schema: Option<RowSchema>,
32 pub effective_search_path: Option<EffectiveSearchPath>,
33 pub dependencies: PreparedAnalysisDependencies,
34 pub dependency_snapshot: Option<PreparedDependencySnapshot>,
35}
36
37pub fn analyze_definition(
38 context: &PreparedDefinitionContext<'_>,
39 mut logical_plan: UnifiedPlan,
40 declared: &[ColumnType],
41) -> Result<PreparedDefinition, SQLError> {
42 let parameter_types =
43 super::declared_parameter_types(context.types, &mut logical_plan, declared)?;
44 let (input, effective_search_path, dependency_snapshot) =
45 with_scope_result(context.scopes, |scope| {
46 let binding = scope.binding_context()?;
47 let mut input = crate::binding::read_prepared_inputs(
48 context.routines,
49 &mut logical_plan,
50 ¶meter_types,
51 &binding,
52 Some(context.aliases),
53 )?;
54 crate::schema::dependencies::oid_alias::read_prepared_oid_alias_constants(
55 context.aliases,
56 &mut logical_plan,
57 )?;
58 logical_plan.visit_scalar_expressions(&mut |expression| {
59 input.dependencies.include_expression(expression);
60 });
61 let effective_search_path =
62 binding.catalog.effective_search_path(&binding.resolution)?;
63 let dependency_snapshot = binding
64 .catalog
65 .prepared_dependency_snapshot(&input.dependencies)?;
66 Ok((input, effective_search_path, dependency_snapshot))
67 })?;
68 let result_schema = analyze_result_schema(context, &logical_plan, &input.parameter_types)?;
69 Ok(PreparedDefinition {
70 composite_inputs: super::composites::CompositeInputs::capture(
71 &logical_plan,
72 context.types,
73 )?,
74 logical_plan,
75 parameter_types: input.parameter_types,
76 result_schema,
77 effective_search_path,
78 dependencies: input.dependencies,
79 dependency_snapshot,
80 })
81}
82
83pub fn analysis_is_current(
85 context: &PreparedDefinitionContext<'_>,
86 retained_path: Option<&EffectiveSearchPath>,
87 dependencies: &PreparedAnalysisDependencies,
88 retained_snapshot: Option<&PreparedDependencySnapshot>,
89) -> Result<bool, SQLError> {
90 if retained_path.is_none() && retained_snapshot.is_none() {
91 return Ok(true);
92 }
93 with_scope_result(context.scopes, |scope| {
94 let binding = scope.binding_context()?;
95 if let Some(path) = retained_path {
96 if binding
97 .catalog
98 .effective_search_path(&binding.resolution)?
99 .as_ref()
100 != Some(path)
101 {
102 return Ok(false);
103 }
104 }
105 match retained_snapshot {
106 Some(snapshot) => Ok(binding
107 .catalog
108 .prepared_dependency_snapshot(dependencies)?
109 .as_ref()
110 == Some(snapshot)),
111 None => Ok(true),
112 }
113 })
114}
115
116pub fn effective_search_path(
118 context: &PreparedDefinitionContext<'_>,
119) -> Result<Option<EffectiveSearchPath>, SQLError> {
120 with_scope_result(context.scopes, |scope| {
121 let binding = scope.binding_context()?;
122 binding.catalog.effective_search_path(&binding.resolution)
123 })
124}
125
126pub fn analyze_result_schema(
127 context: &PreparedDefinitionContext<'_>,
128 logical_plan: &UnifiedPlan,
129 parameter_types: &[Option<ColumnType>],
130) -> Result<Option<RowSchema>, SQLError> {
131 with_scope_result(context.scopes, |scope| {
132 super::analyze_prepared_plan(
133 context.routines,
134 logical_plan,
135 parameter_types,
136 &scope.binding_context()?,
137 )
138 })
139}
140
141fn with_scope_result<T>(
142 scopes: &dyn StatementAnalysisScopes,
143 mut analyze: impl FnMut(&dyn StatementBindingScope) -> Result<T, SQLError>,
144) -> Result<T, SQLError> {
145 let mut result = None;
146 scopes.with_scope(&mut |scope| {
147 result = Some(analyze(scope)?);
148 Ok(())
149 })?;
150 result.ok_or_else(|| {
151 SQLError::Internal("prepared analysis scope did not invoke its operation".into())
152 })
153}
154
155#[cfg(test)]
156mod tests;