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