uqa_sql/routines/
dependencies.rs1use super::{compilation::RoutineCompilationContext, CompiledFunctionBody};
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::{
17 bind_catalog_statement_routines, collect_expression_routine_references,
18 CatalogRoutineContext,
19 },
20 },
21 catalog::{resolution::RelationLookupMode, stored_ast},
22 plan::ExpressionPlan,
23 RowSchema, SQLError,
24};
25
26#[derive(Clone, Copy)]
27pub enum RoutineCompilationMode {
28 Definition,
29 Persisted,
30}
31
32pub fn bind_routine_definition_dependencies(
33 context: &RoutineCompilationContext<'_>,
34 sources: &dyn StoredSourceCatalog,
35 def: &mut CreateFunction,
36 mode: RoutineCompilationMode,
37) -> Result<bool, SQLError> {
38 let mut changed = bind_sql_standard_body_relations(context, sources, def, mode)?;
39 for parameter in &mut def.params {
40 let Some(default) = &mut parameter.default else {
41 continue;
42 };
43 let mut plan = ExpressionPlan::lower_with(default.clone(), &|name: &str| {
44 context.catalog.has_registered_aggregate_function(name)
45 });
46 let binding = context.catalog.binding_snapshot()?;
47 bind_expression_plan_routines_for_storage(
48 context.routines,
49 &mut plan,
50 &[],
51 &binding.context(),
52 &RowSchema::default(),
53 )?;
54 let references = collect_expression_routine_references(&plan)?;
55 changed |= stored_ast::bind_stored_expression_routines(default, &references)?;
56 }
57 Ok(changed)
58}
59
60fn bind_sql_standard_body_relations(
61 context: &RoutineCompilationContext<'_>,
62 sources: &dyn StoredSourceCatalog,
63 def: &mut CreateFunction,
64 mode: RoutineCompilationMode,
65) -> Result<bool, SQLError> {
66 let FunctionBody::Statements(statements) = &mut def.body else {
67 return Ok(false);
68 };
69 let mut changed = false;
70 for statement in statements {
71 changed |= bind_stored_statement_relations(
72 context.relations,
73 statement,
74 RelationLookupMode::Dynamic,
75 matches!(mode, RoutineCompilationMode::Persisted),
76 "SQL routine body",
77 )?;
78 changed |=
79 super::merge_columns::bind_stored_merge_target_columns(context.merge, statement)?;
80 changed |= sources.stored_source_columns().bind_statement(statement)?;
81 }
82 Ok(changed)
83}
84
85pub fn bind_sql_standard_body_routines(
86 context: &RoutineCompilationContext<'_>,
87 def: &mut CreateFunction,
88 compiled: &CompiledFunctionBody,
89) -> Result<bool, SQLError> {
90 let FunctionBody::Statements(statements) = &mut def.body else {
91 return Ok(false);
92 };
93 let CompiledFunctionBody::SQL(plans) = compiled else {
94 return Err(SQLError::Internal(format!(
95 "SQL-standard routine `{}` did not compile to SQL plans",
96 def.name
97 )));
98 };
99 if statements.len() != plans.len() {
100 return Err(SQLError::Internal(format!(
101 "SQL-standard routine `{}` has {} statements but {} plans",
102 def.name,
103 statements.len(),
104 plans.len()
105 )));
106 }
107 let mut changed = false;
108 for (statement, plan) in statements.iter_mut().zip(plans) {
109 let binding = context.catalog.binding_snapshot()?;
110 let routines = bind_catalog_statement_routines(
111 &CatalogRoutineContext {
112 routines: context.routines,
113 binding: &binding.context(),
114 },
115 plan,
116 )?;
117 changed |= stored_ast::bind_stored_statement_routines(statement, &routines.references)?;
118 }
119 Ok(changed)
120}