uqa_sql/catalog/stored_ast/
routines.rs1use super::{BTreeSet, Expr, RelationIdentity, SQLError, Statement, StoredAstVisitor};
10
11pub fn bind_stored_statement_routines(
12 statement: &mut Statement,
13 references: &[crate::binding::stored_routines::BoundRoutineReference],
14) -> Result<bool, SQLError> {
15 let mut ignore_relation = |_: &mut String| -> Result<(), SQLError> { Ok(()) };
16 let mut references = references.iter();
17 let mut changed = false;
18 let mut bind = |name: &mut String,
19 binding: Option<&mut Option<crate::ast::FunctionBinding>>| {
20 let reference = references.next().ok_or_else(|| {
21 SQLError::Internal(format!(
22 "stored catalog routine binding has no entry for call `{name}`"
23 ))
24 })?;
25 changed |= apply_routine_reference(name, binding, reference)?;
26 Ok(())
27 };
28 StoredAstVisitor {
29 source: None,
30 merge: None,
31 expression: None,
32 ty: None,
33 relation: &mut ignore_relation,
34 routine: &mut bind,
35 }
36 .bind_statement(statement)?;
37 if let Some(reference) = references.next() {
38 return Err(SQLError::Internal(format!(
39 "stored catalog routine binding entry `{}` has no matching call",
40 reference.name
41 )));
42 }
43 Ok(changed)
44}
45
46pub fn rewrite_statement_routine_identity(
47 statement: &mut Statement,
48 target: &crate::ast::FunctionBinding,
49 new_name: &str,
50) -> Result<bool, SQLError> {
51 let mut changed = false;
52 let mut ignore_relation = |_: &mut String| -> Result<(), SQLError> { Ok(()) };
53 let mut rewrite = |name: &mut String,
54 binding: Option<&mut Option<crate::ast::FunctionBinding>>|
55 -> Result<(), SQLError> {
56 let Some(binding) = binding.and_then(Option::as_mut) else {
57 return Ok(());
58 };
59 if crate::routines::function_binding_matches(binding, target) {
60 *name = new_name.to_string();
61 binding.name = new_name.to_string();
62 changed = true;
63 }
64 Ok(())
65 };
66 StoredAstVisitor {
67 source: None,
68 merge: None,
69 expression: None,
70 ty: None,
71 relation: &mut ignore_relation,
72 routine: &mut rewrite,
73 }
74 .bind_statement(statement)?;
75 Ok(changed)
76}
77
78pub fn rewrite_expression_routine_identity(
79 expression: &mut Expr,
80 target: &crate::ast::FunctionBinding,
81 new_name: &str,
82) -> Result<bool, SQLError> {
83 let mut changed = false;
84 let mut ignore_relation = |_: &mut String| -> Result<(), SQLError> { Ok(()) };
85 let mut rewrite = |name: &mut String,
86 binding: Option<&mut Option<crate::ast::FunctionBinding>>|
87 -> Result<(), SQLError> {
88 let Some(binding) = binding.and_then(Option::as_mut) else {
89 return Ok(());
90 };
91 if crate::routines::function_binding_matches(binding, target) {
92 *name = new_name.to_string();
93 binding.name = new_name.to_string();
94 changed = true;
95 }
96 Ok(())
97 };
98 StoredAstVisitor {
99 source: None,
100 merge: None,
101 expression: None,
102 ty: None,
103 relation: &mut ignore_relation,
104 routine: &mut rewrite,
105 }
106 .bind_expr(expression, &BTreeSet::new())?;
107 Ok(changed)
108}
109
110pub fn bind_stored_expression_routines(
111 expression: &mut Expr,
112 references: &[crate::binding::stored_routines::BoundRoutineReference],
113) -> Result<bool, SQLError> {
114 let mut ignore_relation = |_: &mut String| -> Result<(), SQLError> { Ok(()) };
115 let mut references = references.iter();
116 let mut changed = false;
117 let mut bind = |name: &mut String,
118 binding: Option<&mut Option<crate::ast::FunctionBinding>>| {
119 let reference = references.next().ok_or_else(|| {
120 SQLError::Internal(format!(
121 "stored catalog routine binding has no entry for call `{name}`"
122 ))
123 })?;
124 changed |= apply_routine_reference(name, binding, reference)?;
125 Ok(())
126 };
127 StoredAstVisitor {
128 source: None,
129 merge: None,
130 expression: None,
131 ty: None,
132 relation: &mut ignore_relation,
133 routine: &mut bind,
134 }
135 .bind_expr(expression, &BTreeSet::new())?;
136 if let Some(reference) = references.next() {
137 return Err(SQLError::Internal(format!(
138 "stored catalog routine binding entry `{}` has no matching call",
139 reference.name
140 )));
141 }
142 Ok(changed)
143}
144
145pub fn statement_references_routine_identity(
146 statement: &Statement,
147 target: &crate::ast::FunctionBinding,
148) -> Result<bool, SQLError> {
149 let mut statement = statement.clone();
150 let mut found = false;
151 let mut ignore_relation = |_: &mut String| -> Result<(), SQLError> { Ok(()) };
152 let mut inspect = |_: &mut String,
153 binding: Option<&mut Option<crate::ast::FunctionBinding>>|
154 -> Result<(), SQLError> {
155 found |= binding
156 .and_then(|binding| binding.as_ref())
157 .is_some_and(|binding| crate::routines::function_binding_matches(binding, target));
158 Ok(())
159 };
160 StoredAstVisitor {
161 source: None,
162 merge: None,
163 expression: None,
164 ty: None,
165 relation: &mut ignore_relation,
166 routine: &mut inspect,
167 }
168 .bind_statement(&mut statement)?;
169 Ok(found)
170}
171
172pub fn expression_references_routine_identity(
173 expression: &Expr,
174 target: &crate::ast::FunctionBinding,
175) -> Result<bool, SQLError> {
176 let mut expression = expression.clone();
177 let mut found = false;
178 let mut ignore_relation = |_: &mut String| -> Result<(), SQLError> { Ok(()) };
179 let mut inspect = |_: &mut String,
180 binding: Option<&mut Option<crate::ast::FunctionBinding>>|
181 -> Result<(), SQLError> {
182 found |= binding
183 .and_then(|binding| binding.as_ref())
184 .is_some_and(|binding| crate::routines::function_binding_matches(binding, target));
185 Ok(())
186 };
187 StoredAstVisitor {
188 source: None,
189 merge: None,
190 expression: None,
191 ty: None,
192 relation: &mut ignore_relation,
193 routine: &mut inspect,
194 }
195 .bind_expr(&mut expression, &BTreeSet::new())?;
196 Ok(found)
197}
198
199fn apply_routine_reference(
200 name: &mut String,
201 binding: Option<&mut Option<crate::ast::FunctionBinding>>,
202 reference: &crate::binding::stored_routines::BoundRoutineReference,
203) -> Result<bool, SQLError> {
204 let (_, local_name) = RelationIdentity::parse_reference(name).map_err(|error| {
205 SQLError::Internal(format!("decode stored catalog routine `{name}`: {error}"))
206 })?;
207 let (_, reference_local_name) =
208 RelationIdentity::parse_reference(&reference.name).map_err(|error| {
209 SQLError::Internal(format!(
210 "decode bound catalog routine `{}`: {error}",
211 reference.name
212 ))
213 })?;
214 if local_name != reference_local_name {
215 return Err(SQLError::Internal(format!(
216 "stored catalog routine call `{name}` does not match bound call `{}`",
217 reference.name
218 )));
219 }
220 let Some(exact) = &reference.binding else {
221 return Ok(false);
222 };
223 let mut changed = false;
224 if !exact.builtin && name != &exact.name {
225 name.clone_from(&exact.name);
226 changed = true;
227 }
228 if let Some(binding) = binding {
229 if binding.as_ref() != Some(exact) {
230 *binding = Some(exact.clone());
231 changed = true;
232 }
233 }
234 Ok(changed)
235}