1use super::compilation::RoutineCompilationContext;
10use crate::{
11 ast::{CreateFunction, FunctionBody},
12 binding::{
13 bind_expression_plan_routines_for_storage,
14 stored_columns::StoredSourceCatalog,
15 stored_relations::bind_stored_statement_relations,
16 stored_routines::{bind_catalog_statement_routines, CatalogRoutineContext},
17 syntax_sites::expression_syntax_sites,
18 },
19 catalog::{resolution::RelationLookupMode, stored_ast},
20 plan::ExpressionPlan,
21 RowSchema, SQLError,
22};
23
24#[derive(Clone, Copy)]
25pub enum RoutineCompilationMode {
26 Definition,
27 Persisted,
28}
29
30pub fn bind_routine_definition_dependencies(
31 context: &RoutineCompilationContext<'_>,
32 sources: &dyn StoredSourceCatalog,
33 def: &mut CreateFunction,
34 mode: RoutineCompilationMode,
35) -> Result<bool, SQLError> {
36 let mut changed = false;
37 if matches!(mode, RoutineCompilationMode::Persisted) {
38 if let FunctionBody::Statements(statements) = &mut def.body {
39 for statement in statements {
40 changed |= crate::binding::legacy_patterns::restore_statement(
41 statement,
42 context.routines,
43 )?;
44 }
45 }
46 for default in def
47 .params
48 .iter_mut()
49 .filter_map(|parameter| parameter.default.as_mut())
50 {
51 changed |=
52 crate::binding::legacy_patterns::restore_expression(default, context.routines)
53 .map_err(SQLError::Internal)?;
54 }
55 }
56 changed |= bind_sql_standard_body_relations(context, sources, def, mode)?;
57 for parameter in &mut def.params {
58 let Some(default) = &mut parameter.default else {
59 continue;
60 };
61 let lowered = ExpressionPlan::lower_with(default.clone(), &|name: &str| {
62 context.catalog.has_registered_aggregate_function(name)
63 });
64 let mut plan = lowered.clone();
65 let binding = context.catalog.binding_snapshot()?;
66 let default_type = bind_expression_plan_routines_for_storage(
67 context.routines,
68 &mut plan,
69 &[],
70 &binding.context(),
71 &RowSchema::default(),
72 )?;
73 let default_type =
74 if crate::type_resolution::routine_polymorphic_type(¶meter.type_name).is_some() {
75 default_type
76 } else {
77 Some(
78 context
79 .types
80 .resolve_catalog_column_type_name(¶meter.type_name)?
81 .without_type_modifiers(),
82 )
83 };
84 let default_type =
85 super::defaults::default_expression_type(¶meter.type_name, default, default_type);
86 if parameter.default_type != default_type {
87 parameter.default_type = default_type;
88 changed = true;
89 }
90 let sites = expression_syntax_sites(&lowered, &plan)?;
91 changed |= stored_ast::bind_stored_expression_sites(default, &sites)?;
92 if let Some(ty) = user_defined_type(context, ¶meter.type_name)? {
94 changed |= stored_ast::fold_assigned_stored_literal(
95 default,
96 &ty,
97 context.routines.enum_labels(),
98 )?;
99 }
100 }
101 Ok(changed)
102}
103
104fn bind_sql_standard_body_relations(
105 context: &RoutineCompilationContext<'_>,
106 sources: &dyn StoredSourceCatalog,
107 def: &mut CreateFunction,
108 mode: RoutineCompilationMode,
109) -> Result<bool, SQLError> {
110 let FunctionBody::Statements(statements) = &mut def.body else {
111 return Ok(false);
112 };
113 let mut changed = false;
114 for statement in statements {
115 super::compilation::validate_sql_standard_statement(statement)?;
116 changed |= bind_stored_statement_relations(
117 context.relations,
118 statement,
119 match mode {
120 RoutineCompilationMode::Definition => RelationLookupMode::Dynamic,
121 RoutineCompilationMode::Persisted => RelationLookupMode::Bound,
122 },
123 matches!(mode, RoutineCompilationMode::Persisted),
124 "SQL routine body",
125 )?;
126 changed |=
127 super::merge_columns::bind_stored_merge_target_columns(context.merge, statement)?;
128 changed |= sources.stored_source_columns().bind_statement(statement)?;
129 }
130 Ok(changed)
131}
132
133pub fn bind_sql_standard_body_routines(
135 context: &RoutineCompilationContext<'_>,
136 def: &mut CreateFunction,
137 mode: RoutineCompilationMode,
138) -> Result<bool, SQLError> {
139 if !matches!(def.body, FunctionBody::Statements(_)) {
140 return Ok(false);
141 }
142 super::compilation::validate_routine_signature(context, def)?;
144 let positional = super::body_validation::routine_parameter_values(context.types, def);
145 let parameters = super::body_parameters::sql_body_parameter_scope(def, &positional)?;
146 let result_types = def_result_types(context, &def.params, &def.returns)?;
147 let FunctionBody::Statements(statements) = &mut def.body else {
148 return Ok(false);
149 };
150 let lowering = super::compilation::SQLRoutineLowering {
151 bind_catalog_dependencies: true,
152 persisted_definition: matches!(mode, RoutineCompilationMode::Persisted),
153 preserve_target_expressions: true,
154 };
155 let mut changed = false;
156 for statement in statements.iter_mut() {
157 let mut lowered =
158 super::compilation::lower_sql_routine_statement(context, statement.clone(), lowering)?;
159 let binding = context.catalog.binding_snapshot()?;
160 crate::binding::bind_routine_parameter_references(
161 context.routines,
162 &mut lowered,
163 &positional,
164 &binding.context(),
165 ¶meters,
166 )?;
167 let routines = bind_catalog_statement_routines(
168 &CatalogRoutineContext {
169 routines: context.routines,
170 binding: &binding.context(),
171 },
172 &lowered,
173 &positional,
174 )?;
175 changed |= stored_ast::bind_stored_statement_sites(statement, &routines.sites)?;
176 }
177 if let Some(statement) = statements.last_mut() {
178 changed |= fold_sql_function_result(context, result_types, statement)?;
179 }
180 Ok(changed)
181}
182
183fn user_defined_type(
185 context: &RoutineCompilationContext<'_>,
186 name: &str,
187) -> Result<Option<crate::ColumnType>, SQLError> {
188 if crate::ast::UserTypeIdentity::parse(name).is_none() {
189 return Ok(None);
190 }
191 context.routines.resolve_type_name(name)
192}
193
194fn def_result_types(
196 context: &RoutineCompilationContext<'_>,
197 params: &[crate::ast::FunctionParam],
198 returns: &crate::ast::FunctionReturns,
199) -> Result<Vec<Option<crate::ColumnType>>, SQLError> {
200 use crate::ast::{FunctionParamMode, FunctionReturns};
201 let outputs = params
202 .iter()
203 .filter(|parameter| {
204 matches!(
205 parameter.mode,
206 FunctionParamMode::Out | FunctionParamMode::InOut | FunctionParamMode::Table
207 )
208 })
209 .collect::<Vec<_>>();
210 let names = if outputs.is_empty() {
211 match returns {
212 FunctionReturns::Scalar { type_name } | FunctionReturns::SetOf { type_name } => {
213 vec![type_name.as_str()]
214 }
215 FunctionReturns::None | FunctionReturns::Table => Vec::new(),
216 }
217 } else {
218 outputs
219 .iter()
220 .map(|parameter| parameter.type_name.as_str())
221 .collect()
222 };
223 names
224 .into_iter()
225 .map(|name| user_defined_type(context, name))
226 .collect()
227}
228
229fn fold_sql_function_result(
231 context: &RoutineCompilationContext<'_>,
232 types: Vec<Option<crate::ColumnType>>,
233 statement: &mut crate::ast::Statement,
234) -> Result<bool, SQLError> {
235 let crate::ast::Statement::Select(select) = statement else {
236 return Ok(false);
237 };
238 if types.is_empty()
239 || select.set_op.is_some()
240 || !select.values.is_empty()
241 || select.projections.len() != types.len()
242 {
243 return Ok(false);
244 }
245 let mut changed = false;
246 for (projection, ty) in select.projections.iter_mut().zip(&types) {
247 let Some(ty) = ty else {
248 continue;
249 };
250 changed |= stored_ast::fold_assigned_stored_literal(
251 &mut projection.expr,
252 ty,
253 context.routines.enum_labels(),
254 )?;
255 }
256 Ok(changed)
257}