1use crate::ast::FunctionBinding;
10use crate::{
11 plan::{QueryPlan, RelationalPlan, SourcePlan},
12 ScalarExpr,
13};
14use uqa_core::{RelationIdentity, Value};
15
16pub fn canonical_virtual_relation_reference(reference: &str) -> Option<String> {
17 crate::catalog::resolve_virtual_relation(&[], reference)
18 .map(crate::catalog::VirtualRelation::qualified_name)
19}
20
21pub fn sequence_function_reference_mut(expression: &mut ScalarExpr) -> Option<&mut String> {
22 let ScalarExpr::Func { name, args, .. } = expression else {
23 return None;
24 };
25 let lower = name.to_ascii_lowercase();
26 let local = lower.strip_prefix("pg_catalog.").unwrap_or(&lower);
27 if !matches!(local, "nextval" | "currval" | "setval")
28 || (lower.contains('.') && !lower.starts_with("pg_catalog."))
29 {
30 return None;
31 }
32 regclass_literal_mut(args.first_mut()?)
33}
34
35pub fn regclass_literal_mut(expression: &mut ScalarExpr) -> Option<&mut String> {
36 match expression {
37 ScalarExpr::Literal(Value::Str(reference)) => Some(reference),
38 ScalarExpr::Cast { expr, ty, .. }
39 if ty.eq_ignore_ascii_case("regclass")
40 || ty.eq_ignore_ascii_case("pg_catalog.regclass") =>
41 {
42 regclass_literal_mut(expr)
43 }
44 _ => None,
45 }
46}
47
48pub fn bind_query_plan_sequence_references<E>(
49 plan: &mut QueryPlan,
50 resolve: &mut impl FnMut(&str) -> Result<String, E>,
51) -> Result<(), E> {
52 let mut error = None;
53 plan.rewrite_scalar_expressions(&mut |expression| {
54 if error.is_some() {
55 return;
56 }
57 let Some(reference) = sequence_function_reference_mut(expression) else {
58 return;
59 };
60 match resolve(reference) {
61 Ok(canonical) => *reference = canonical,
62 Err(binding_error) => error = Some(binding_error),
63 }
64 });
65 error.map_or(Ok(()), Err)
66}
67
68pub fn bind_query_plan_relations<E>(
69 plan: &mut QueryPlan,
70 inherited_ctes: &std::collections::BTreeSet<String>,
71 resolve: &mut impl FnMut(&str) -> Result<String, E>,
72) -> Result<(), E> {
73 bind_query_plan_relation_targets(plan, inherited_ctes, &mut |name, _| resolve(name))
74}
75
76pub fn bind_query_plan_relation_targets<E>(
78 plan: &mut QueryPlan,
79 inherited_ctes: &std::collections::BTreeSet<String>,
80 resolve: &mut impl FnMut(&str, bool) -> Result<String, E>,
81) -> Result<(), E> {
82 let mut visible_ctes = inherited_ctes.clone();
84 let recursive_ctes = plan.ctes.iter().any(|cte| cte.recursive).then(|| {
85 plan.ctes
86 .iter()
87 .map(|cte| cte.name.clone())
88 .collect::<std::collections::BTreeSet<_>>()
89 });
90 for cte in &mut plan.ctes {
91 let body_ctes = recursive_ctes.as_ref().map_or_else(
92 || visible_ctes.clone(),
93 |ctes| inherited_ctes.union(ctes).cloned().collect(),
94 );
95 bind_cte_relation_targets(&mut cte.body, &body_ctes, resolve)?;
96 visible_ctes.insert(cte.name.clone());
97 }
98 bind_relational_targets(&mut plan.root, &visible_ctes, resolve)?;
99 plan.relations_bound = true;
100 Ok(())
101}
102
103pub fn bind_relational_plan_relations<E>(
104 plan: &mut RelationalPlan,
105 visible_ctes: &std::collections::BTreeSet<String>,
106 resolve: &mut impl FnMut(&str) -> Result<String, E>,
107) -> Result<(), E> {
108 bind_relational_targets(plan, visible_ctes, &mut |name, _| resolve(name))
109}
110
111fn bind_relational_targets<E>(
112 plan: &mut RelationalPlan,
113 visible_ctes: &std::collections::BTreeSet<String>,
114 resolve: &mut impl FnMut(&str, bool) -> Result<String, E>,
115) -> Result<(), E> {
116 match plan {
117 RelationalPlan::QueryBlock(block) => {
118 if let Some(source) = &mut block.from {
119 bind_source_targets(source, visible_ctes, resolve)?;
120 }
121 for subquery in &mut block.subqueries {
122 bind_query_plan_relation_targets(subquery, visible_ctes, resolve)?;
123 }
124 }
125 RelationalPlan::SetOp {
126 left,
127 right,
128 subqueries,
129 ..
130 } => {
131 bind_query_plan_relation_targets(left, visible_ctes, resolve)?;
132 bind_query_plan_relation_targets(right, visible_ctes, resolve)?;
133 for subquery in subqueries {
134 bind_query_plan_relation_targets(subquery, visible_ctes, resolve)?;
135 }
136 }
137 RelationalPlan::Values { subqueries, .. } => {
138 for subquery in subqueries {
139 bind_query_plan_relation_targets(subquery, visible_ctes, resolve)?;
140 }
141 }
142 }
143 Ok(())
144}
145
146pub fn bind_source_plan_relations<E>(
147 source: &mut SourcePlan,
148 visible_ctes: &std::collections::BTreeSet<String>,
149 resolve: &mut impl FnMut(&str) -> Result<String, E>,
150) -> Result<(), E> {
151 bind_source_targets(source, visible_ctes, &mut |name, _| resolve(name))
152}
153
154fn bind_source_targets<E>(
155 source: &mut SourcePlan,
156 visible_ctes: &std::collections::BTreeSet<String>,
157 resolve: &mut impl FnMut(&str, bool) -> Result<String, E>,
158) -> Result<(), E> {
159 match source {
160 SourcePlan::Table {
161 name,
162 qualifier,
163 include_descendants,
164 ..
165 } => {
166 if qualifier.is_empty() {
167 *qualifier = RelationIdentity::parse_reference(name)
168 .map_or_else(|_| name.clone(), |(_, relation)| relation);
169 }
170 let is_cte =
171 RelationIdentity::parse_reference(name)
172 .ok()
173 .is_some_and(|(schema, relation)| {
174 schema.is_none() && visible_ctes.contains(&relation)
175 });
176 if !is_cte {
177 *name = resolve(name, *include_descendants)?;
178 }
179 }
180 SourcePlan::Join { left, right, .. } => {
181 bind_source_targets(left, visible_ctes, resolve)?;
182 bind_source_targets(right, visible_ctes, resolve)?;
183 }
184 SourcePlan::Subquery { body, .. } => {
185 bind_query_plan_relation_targets(body, visible_ctes, resolve)?;
186 }
187 SourcePlan::Function {
188 name,
189 output_name,
190 relations,
191 ..
192 } => {
193 if output_name.is_empty() {
194 *output_name = RelationIdentity::parse_reference(name)
195 .map_or_else(|_| name.clone(), |(_, function)| function);
196 }
197 if let Some(relations) = relations {
198 relations.left = resolve(&relations.left, false)?;
199 relations.right = resolve(&relations.right, false)?;
200 }
201 }
202 SourcePlan::FunctionGroup { functions, .. } => {
203 for function in functions {
204 if function.output_name.is_empty() {
205 function.output_name = RelationIdentity::parse_reference(&function.name)
206 .map_or_else(|_| function.name.clone(), |(_, name)| name);
207 }
208 if let Some(relations) = &mut function.relations {
209 relations.left = resolve(&relations.left, false)?;
210 relations.right = resolve(&relations.right, false)?;
211 }
212 }
213 }
214 SourcePlan::Values { .. } => {}
215 }
216 Ok(())
217}
218
219pub fn relation_reference_matches(reference: &str, target: &RelationIdentity) -> bool {
220 match RelationIdentity::parse_reference(reference) {
221 Ok((Some(schema), name)) => schema == target.schema && name == target.name,
222 Ok((None, name)) => name == target.name,
223 Err(_) => true,
226 }
227}
228
229pub fn source_plan_references_relation(
230 source: &crate::plan::SourcePlan,
231 target: &RelationIdentity,
232 ctes: &std::collections::BTreeSet<String>,
233) -> bool {
234 match source {
235 crate::plan::SourcePlan::Table { name, .. } => {
236 let is_cte = RelationIdentity::parse_reference(name)
237 .ok()
238 .is_some_and(|(schema, relation)| schema.is_none() && ctes.contains(&relation));
239 !is_cte && relation_reference_matches(name, target)
240 }
241 crate::plan::SourcePlan::Join { left, right, .. } => {
242 source_plan_references_relation(left, target, ctes)
243 || source_plan_references_relation(right, target, ctes)
244 }
245 crate::plan::SourcePlan::Subquery { body, .. } => {
246 query_plan_references_relation(body, target, ctes)
247 }
248 crate::plan::SourcePlan::Function { relations, .. } => {
249 relations.as_ref().is_some_and(|relations| {
250 relation_reference_matches(&relations.left, target)
251 || relation_reference_matches(&relations.right, target)
252 })
253 }
254 crate::plan::SourcePlan::FunctionGroup { functions, .. } => {
255 functions.iter().any(|function| {
256 function.relations.as_ref().is_some_and(|relations| {
257 relation_reference_matches(&relations.left, target)
258 || relation_reference_matches(&relations.right, target)
259 })
260 })
261 }
262 crate::plan::SourcePlan::Values { .. } => false,
263 }
264}
265
266pub fn query_plan_references_relation(
267 query: &crate::plan::QueryPlan,
268 target: &RelationIdentity,
269 inherited_ctes: &std::collections::BTreeSet<String>,
270) -> bool {
271 let mut ctes = inherited_ctes.clone();
272 ctes.extend(query.ctes.iter().map(|cte| cte.name.clone()));
273 if query
274 .ctes
275 .iter()
276 .any(|cte| cte_plan_references_relation(&cte.body, target, &ctes))
277 {
278 return true;
279 }
280 match &query.root {
281 crate::plan::RelationalPlan::QueryBlock(block) => {
282 block
283 .from
284 .as_ref()
285 .is_some_and(|source| source_plan_references_relation(source, target, &ctes))
286 || block
287 .subqueries
288 .iter()
289 .any(|query| query_plan_references_relation(query, target, &ctes))
290 }
291 crate::plan::RelationalPlan::SetOp {
292 left,
293 right,
294 subqueries,
295 ..
296 } => {
297 query_plan_references_relation(left, target, &ctes)
298 || query_plan_references_relation(right, target, &ctes)
299 || subqueries
300 .iter()
301 .any(|query| query_plan_references_relation(query, target, &ctes))
302 }
303 crate::plan::RelationalPlan::Values { subqueries, .. } => subqueries
304 .iter()
305 .any(|query| query_plan_references_relation(query, target, &ctes)),
306 }
307}
308
309pub fn query_plan_references_sequence(plan: &QueryPlan, target: &RelationIdentity) -> bool {
310 let mut plan = plan.clone();
311 let mut referenced = false;
312 plan.rewrite_scalar_expressions(&mut |expression| {
313 if let Some(reference) = sequence_function_reference_mut(expression) {
314 referenced |= relation_reference_matches(reference, target);
315 }
316 });
317 referenced
318}
319
320pub use crate::routines::function_binding_matches;
321
322fn function_binding_needs_object_identity(binding: &FunctionBinding) -> bool {
323 !binding.builtin
324 && binding.dispatch.is_none()
325 && binding.resolution_error.is_none()
326 && binding.object_id.is_none()
327}
328
329fn source_plan_has_legacy_routine_identity(source: &SourcePlan) -> bool {
330 match source {
331 SourcePlan::Table { .. } | SourcePlan::Values { .. } => false,
332 SourcePlan::Join { left, right, .. } => {
333 source_plan_has_legacy_routine_identity(left)
334 || source_plan_has_legacy_routine_identity(right)
335 }
336 SourcePlan::Subquery { body, .. } => query_plan_sources_have_legacy_routine_identity(body),
337 SourcePlan::Function { binding, .. } => binding
338 .as_ref()
339 .is_some_and(function_binding_needs_object_identity),
340 SourcePlan::FunctionGroup { functions, .. } => functions.iter().any(|function| {
341 function
342 .binding
343 .as_ref()
344 .is_some_and(function_binding_needs_object_identity)
345 }),
346 }
347}
348
349fn relational_plan_has_legacy_routine_identity(plan: &RelationalPlan) -> bool {
350 match plan {
351 RelationalPlan::QueryBlock(block) => {
352 block
353 .from
354 .as_ref()
355 .is_some_and(source_plan_has_legacy_routine_identity)
356 || block
357 .subqueries
358 .iter()
359 .any(query_plan_sources_have_legacy_routine_identity)
360 }
361 RelationalPlan::SetOp {
362 left,
363 right,
364 subqueries,
365 ..
366 } => {
367 query_plan_sources_have_legacy_routine_identity(left)
368 || query_plan_sources_have_legacy_routine_identity(right)
369 || subqueries
370 .iter()
371 .any(query_plan_sources_have_legacy_routine_identity)
372 }
373 RelationalPlan::Values { subqueries, .. } => subqueries
374 .iter()
375 .any(query_plan_sources_have_legacy_routine_identity),
376 }
377}
378
379fn query_plan_sources_have_legacy_routine_identity(plan: &QueryPlan) -> bool {
380 plan.ctes.iter().any(|cte| {
381 cte_relational_inputs_any(
382 &cte.body,
383 &query_plan_sources_have_legacy_routine_identity,
384 &source_plan_has_legacy_routine_identity,
385 )
386 }) || relational_plan_has_legacy_routine_identity(&plan.root)
387}
388
389pub fn query_plan_has_legacy_routine_identity(plan: &QueryPlan) -> bool {
390 let mut scalar_plan = plan.clone();
391 let mut legacy = false;
392 scalar_plan.rewrite_scalar_expressions(&mut |expression| {
393 if let ScalarExpr::Func {
394 name,
395 binding,
396 order_syntax,
397 order_by,
398 ..
399 } = expression
400 {
401 legacy |= binding
402 .as_ref()
403 .is_some_and(function_binding_needs_object_identity);
404 legacy |= *order_syntax == crate::ast::FunctionOrderSyntax::Legacy
407 && super::ordered_calls::uses_ordered_arguments(
408 *order_syntax,
409 name,
410 binding.as_ref(),
411 order_by.len(),
412 );
413 }
414 });
415 legacy || query_plan_sources_have_legacy_routine_identity(plan)
416}
417
418fn source_plan_references_function(source: &SourcePlan, target: &FunctionBinding) -> bool {
419 match source {
420 SourcePlan::Table { .. } | SourcePlan::Values { .. } => false,
421 SourcePlan::Join { left, right, .. } => {
422 source_plan_references_function(left, target)
423 || source_plan_references_function(right, target)
424 }
425 SourcePlan::Subquery { body, .. } => query_plan_sources_reference_function(body, target),
426 SourcePlan::Function { binding, .. } => binding
427 .as_ref()
428 .is_some_and(|binding| function_binding_matches(binding, target)),
429 SourcePlan::FunctionGroup { functions, .. } => functions.iter().any(|function| {
430 function
431 .binding
432 .as_ref()
433 .is_some_and(|binding| function_binding_matches(binding, target))
434 }),
435 }
436}
437
438fn relational_plan_references_function(plan: &RelationalPlan, target: &FunctionBinding) -> bool {
439 match plan {
440 RelationalPlan::QueryBlock(block) => {
441 block
442 .from
443 .as_ref()
444 .is_some_and(|source| source_plan_references_function(source, target))
445 || block
446 .subqueries
447 .iter()
448 .any(|query| query_plan_sources_reference_function(query, target))
449 }
450 RelationalPlan::SetOp {
451 left,
452 right,
453 subqueries,
454 ..
455 } => {
456 query_plan_sources_reference_function(left, target)
457 || query_plan_sources_reference_function(right, target)
458 || subqueries
459 .iter()
460 .any(|query| query_plan_sources_reference_function(query, target))
461 }
462 RelationalPlan::Values { subqueries, .. } => subqueries
463 .iter()
464 .any(|query| query_plan_sources_reference_function(query, target)),
465 }
466}
467
468fn query_plan_sources_reference_function(plan: &QueryPlan, target: &FunctionBinding) -> bool {
469 plan.ctes.iter().any(|cte| {
470 cte_relational_inputs_any(
471 &cte.body,
472 &|query| query_plan_sources_reference_function(query, target),
473 &|source| source_plan_references_function(source, target),
474 )
475 }) || relational_plan_references_function(&plan.root, target)
476}
477
478pub fn query_plan_references_function(plan: &QueryPlan, target: &FunctionBinding) -> bool {
479 let mut scalar_plan = plan.clone();
480 let mut referenced = false;
481 scalar_plan.rewrite_scalar_expressions(&mut |expression| {
482 if let ScalarExpr::Func {
483 binding: Some(binding),
484 ..
485 } = expression
486 {
487 referenced |= function_binding_matches(binding, target);
488 }
489 });
490 referenced || query_plan_sources_reference_function(plan, target)
491}
492
493fn rewrite_source_plan_routine_identity(
494 source: &mut SourcePlan,
495 target: &FunctionBinding,
496 new_name: &str,
497) -> bool {
498 match source {
499 SourcePlan::Table { .. } | SourcePlan::Values { .. } => false,
500 SourcePlan::Join { left, right, .. } => {
501 rewrite_source_plan_routine_identity(left, target, new_name)
502 | rewrite_source_plan_routine_identity(right, target, new_name)
503 }
504 SourcePlan::Subquery { body, .. } => {
505 rewrite_query_source_routine_identity(body, target, new_name)
506 }
507 SourcePlan::Function { name, binding, .. } => {
508 let Some(binding) = binding.as_mut() else {
509 return false;
510 };
511 if !function_binding_matches(binding, target) {
512 return false;
513 }
514 *name = new_name.to_string();
515 binding.name = new_name.to_string();
516 true
517 }
518 SourcePlan::FunctionGroup { functions, .. } => {
519 let mut changed = false;
520 for function in functions {
521 let Some(binding) = function.binding.as_mut() else {
522 continue;
523 };
524 if function_binding_matches(binding, target) {
525 function.name = new_name.to_string();
526 binding.name = new_name.to_string();
527 changed = true;
528 }
529 }
530 changed
531 }
532 }
533}
534
535fn rewrite_relational_plan_source_routine_identity(
536 plan: &mut RelationalPlan,
537 target: &FunctionBinding,
538 new_name: &str,
539) -> bool {
540 match plan {
541 RelationalPlan::QueryBlock(block) => {
542 let mut changed = block.from.as_mut().is_some_and(|source| {
543 rewrite_source_plan_routine_identity(source, target, new_name)
544 });
545 for subquery in &mut block.subqueries {
546 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
547 }
548 changed
549 }
550 RelationalPlan::SetOp {
551 left,
552 right,
553 subqueries,
554 ..
555 } => {
556 let mut changed = rewrite_query_source_routine_identity(left, target, new_name)
557 | rewrite_query_source_routine_identity(right, target, new_name);
558 for subquery in subqueries {
559 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
560 }
561 changed
562 }
563 RelationalPlan::Values { subqueries, .. } => {
564 let mut changed = false;
565 for subquery in subqueries {
566 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
567 }
568 changed
569 }
570 }
571}
572
573fn rewrite_query_source_routine_identity(
574 plan: &mut QueryPlan,
575 target: &FunctionBinding,
576 new_name: &str,
577) -> bool {
578 let mut changed = false;
579 for cte in &mut plan.ctes {
580 changed |= rewrite_cte_source_routine_identity(&mut cte.body, target, new_name);
581 }
582 changed | rewrite_relational_plan_source_routine_identity(&mut plan.root, target, new_name)
583}
584
585pub fn rewrite_query_plan_routine_identity(
586 plan: &mut QueryPlan,
587 target: &FunctionBinding,
588 new_name: &str,
589) -> bool {
590 let mut changed = false;
591 plan.rewrite_scalar_expressions(&mut |expression| {
592 let ScalarExpr::Func {
593 name,
594 binding: Some(binding),
595 ..
596 } = expression
597 else {
598 return;
599 };
600 if function_binding_matches(binding, target) {
601 *name = new_name.to_string();
602 binding.name = new_name.to_string();
603 changed = true;
604 }
605 });
606 changed | rewrite_query_source_routine_identity(plan, target, new_name)
607}
608
609pub fn bind_cte_plan_relations<E>(
610 body: &mut crate::plan::CtePlanBody,
611 inherited: &std::collections::BTreeSet<String>,
612 resolve: &mut impl FnMut(&str) -> Result<String, E>,
613) -> Result<(), E> {
614 bind_cte_relation_targets(body, inherited, &mut |name, _| resolve(name))
615}
616
617fn bind_cte_relation_targets<E>(
618 body: &mut crate::plan::CtePlanBody,
619 inherited: &std::collections::BTreeSet<String>,
620 resolve: &mut impl FnMut(&str, bool) -> Result<String, E>,
621) -> Result<(), E> {
622 let crate::plan::CtePlanBody::Command(command) = body else {
623 let crate::plan::CtePlanBody::Query(query) = body else {
624 unreachable!()
625 };
626 return bind_query_plan_relation_targets(query, inherited, resolve);
627 };
628 let include_descendants = match command.as_ref() {
629 crate::plan::CommandPlan::Update(plan) => plan.include_descendants,
630 crate::plan::CommandPlan::Delete(plan) => plan.include_descendants,
631 _ => false,
632 };
633 if let Some(target) = command.mutation_target_mut() {
634 *target = resolve(target, include_descendants)?;
635 }
636 match command.as_mut() {
637 crate::plan::CommandPlan::Insert(plan) => {
638 plan.target_relation_bound = true;
639 plan.relations_bound = true;
640 }
641 crate::plan::CommandPlan::Update(plan) => {
642 plan.target_relation_bound = true;
643 plan.relations_bound = true;
644 }
645 crate::plan::CommandPlan::Delete(plan) => {
646 plan.target_relation_bound = true;
647 plan.relations_bound = true;
648 }
649 _ => {}
650 }
651 let mut visible = inherited.clone();
652 if let Some(ctes) = command.ctes_mut() {
653 let recursive = ctes.iter().any(|cte| cte.recursive).then(|| {
654 ctes.iter()
655 .map(|cte| cte.name.clone())
656 .collect::<std::collections::BTreeSet<_>>()
657 });
658 for cte in ctes {
659 let scope = recursive.as_ref().map_or_else(
660 || visible.clone(),
661 |names| inherited.union(names).cloned().collect(),
662 );
663 bind_cte_relation_targets(&mut cte.body, &scope, resolve)?;
664 visible.insert(cte.name.clone());
665 }
666 }
667 if let Some(source) = command.source_input_mut() {
668 bind_source_targets(source, &visible, resolve)?;
669 }
670 for query in command.query_inputs_mut() {
671 bind_query_plan_relation_targets(query, &visible, resolve)?;
672 }
673 Ok(())
674}
675
676fn cte_plan_references_relation(
677 body: &crate::plan::CtePlanBody,
678 target: &RelationIdentity,
679 inherited: &std::collections::BTreeSet<String>,
680) -> bool {
681 match body {
682 crate::plan::CtePlanBody::Query(query) => {
683 query_plan_references_relation(query, target, inherited)
684 }
685 crate::plan::CtePlanBody::Command(command) => {
686 let mut visible = inherited.clone();
687 visible.extend(command.ctes().iter().map(|cte| cte.name.clone()));
688 command
689 .mutation_target()
690 .is_some_and(|name| relation_reference_matches(name, target))
691 || command
692 .ctes()
693 .iter()
694 .any(|cte| cte_plan_references_relation(&cte.body, target, &visible))
695 || command
696 .source_input()
697 .is_some_and(|source| source_plan_references_relation(source, target, &visible))
698 || command
699 .query_inputs()
700 .iter()
701 .any(|query| query_plan_references_relation(query, target, &visible))
702 }
703 }
704}
705
706fn cte_relational_inputs_any(
707 body: &crate::plan::CtePlanBody,
708 query: &dyn Fn(&QueryPlan) -> bool,
709 source: &dyn Fn(&crate::plan::SourcePlan) -> bool,
710) -> bool {
711 match body {
712 crate::plan::CtePlanBody::Query(plan) => query(plan),
713 crate::plan::CtePlanBody::Command(command) => {
714 command
715 .ctes()
716 .iter()
717 .any(|cte| cte_relational_inputs_any(&cte.body, query, source))
718 || command.query_inputs().iter().any(|plan| query(plan))
719 || command.source_input().is_some_and(source)
720 }
721 }
722}
723
724fn rewrite_cte_source_routine_identity(
725 body: &mut crate::plan::CtePlanBody,
726 target: &FunctionBinding,
727 new_name: &str,
728) -> bool {
729 match body {
730 crate::plan::CtePlanBody::Query(query) => {
731 rewrite_query_source_routine_identity(query, target, new_name)
732 }
733 crate::plan::CtePlanBody::Command(command) => {
734 let mut changed = false;
735 if let Some(ctes) = command.ctes_mut() {
736 for cte in ctes {
737 changed |= rewrite_cte_source_routine_identity(&mut cte.body, target, new_name);
738 }
739 }
740 if let Some(source) = command.source_input_mut() {
741 changed |= rewrite_source_plan_routine_identity(source, target, new_name);
742 }
743 for query in command.query_inputs_mut() {
744 changed |= rewrite_query_source_routine_identity(query, target, new_name);
745 }
746 changed
747 }
748 }
749}
750
751pub mod restoration;