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 binding: Some(binding),
395 ..
396 } = expression
397 {
398 legacy |= function_binding_needs_object_identity(binding);
399 }
400 });
401 legacy || query_plan_sources_have_legacy_routine_identity(plan)
402}
403
404fn source_plan_references_function(source: &SourcePlan, target: &FunctionBinding) -> bool {
405 match source {
406 SourcePlan::Table { .. } | SourcePlan::Values { .. } => false,
407 SourcePlan::Join { left, right, .. } => {
408 source_plan_references_function(left, target)
409 || source_plan_references_function(right, target)
410 }
411 SourcePlan::Subquery { body, .. } => query_plan_sources_reference_function(body, target),
412 SourcePlan::Function { binding, .. } => binding
413 .as_ref()
414 .is_some_and(|binding| function_binding_matches(binding, target)),
415 SourcePlan::FunctionGroup { functions, .. } => functions.iter().any(|function| {
416 function
417 .binding
418 .as_ref()
419 .is_some_and(|binding| function_binding_matches(binding, target))
420 }),
421 }
422}
423
424fn relational_plan_references_function(plan: &RelationalPlan, target: &FunctionBinding) -> bool {
425 match plan {
426 RelationalPlan::QueryBlock(block) => {
427 block
428 .from
429 .as_ref()
430 .is_some_and(|source| source_plan_references_function(source, target))
431 || block
432 .subqueries
433 .iter()
434 .any(|query| query_plan_sources_reference_function(query, target))
435 }
436 RelationalPlan::SetOp {
437 left,
438 right,
439 subqueries,
440 ..
441 } => {
442 query_plan_sources_reference_function(left, target)
443 || query_plan_sources_reference_function(right, target)
444 || subqueries
445 .iter()
446 .any(|query| query_plan_sources_reference_function(query, target))
447 }
448 RelationalPlan::Values { subqueries, .. } => subqueries
449 .iter()
450 .any(|query| query_plan_sources_reference_function(query, target)),
451 }
452}
453
454fn query_plan_sources_reference_function(plan: &QueryPlan, target: &FunctionBinding) -> bool {
455 plan.ctes.iter().any(|cte| {
456 cte_relational_inputs_any(
457 &cte.body,
458 &|query| query_plan_sources_reference_function(query, target),
459 &|source| source_plan_references_function(source, target),
460 )
461 }) || relational_plan_references_function(&plan.root, target)
462}
463
464pub fn query_plan_references_function(plan: &QueryPlan, target: &FunctionBinding) -> bool {
465 let mut scalar_plan = plan.clone();
466 let mut referenced = false;
467 scalar_plan.rewrite_scalar_expressions(&mut |expression| {
468 if let ScalarExpr::Func {
469 binding: Some(binding),
470 ..
471 } = expression
472 {
473 referenced |= function_binding_matches(binding, target);
474 }
475 });
476 referenced || query_plan_sources_reference_function(plan, target)
477}
478
479fn rewrite_source_plan_routine_identity(
480 source: &mut SourcePlan,
481 target: &FunctionBinding,
482 new_name: &str,
483) -> bool {
484 match source {
485 SourcePlan::Table { .. } | SourcePlan::Values { .. } => false,
486 SourcePlan::Join { left, right, .. } => {
487 rewrite_source_plan_routine_identity(left, target, new_name)
488 | rewrite_source_plan_routine_identity(right, target, new_name)
489 }
490 SourcePlan::Subquery { body, .. } => {
491 rewrite_query_source_routine_identity(body, target, new_name)
492 }
493 SourcePlan::Function { name, binding, .. } => {
494 let Some(binding) = binding.as_mut() else {
495 return false;
496 };
497 if !function_binding_matches(binding, target) {
498 return false;
499 }
500 *name = new_name.to_string();
501 binding.name = new_name.to_string();
502 true
503 }
504 SourcePlan::FunctionGroup { functions, .. } => {
505 let mut changed = false;
506 for function in functions {
507 let Some(binding) = function.binding.as_mut() else {
508 continue;
509 };
510 if function_binding_matches(binding, target) {
511 function.name = new_name.to_string();
512 binding.name = new_name.to_string();
513 changed = true;
514 }
515 }
516 changed
517 }
518 }
519}
520
521fn rewrite_relational_plan_source_routine_identity(
522 plan: &mut RelationalPlan,
523 target: &FunctionBinding,
524 new_name: &str,
525) -> bool {
526 match plan {
527 RelationalPlan::QueryBlock(block) => {
528 let mut changed = block.from.as_mut().is_some_and(|source| {
529 rewrite_source_plan_routine_identity(source, target, new_name)
530 });
531 for subquery in &mut block.subqueries {
532 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
533 }
534 changed
535 }
536 RelationalPlan::SetOp {
537 left,
538 right,
539 subqueries,
540 ..
541 } => {
542 let mut changed = rewrite_query_source_routine_identity(left, target, new_name)
543 | rewrite_query_source_routine_identity(right, target, new_name);
544 for subquery in subqueries {
545 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
546 }
547 changed
548 }
549 RelationalPlan::Values { subqueries, .. } => {
550 let mut changed = false;
551 for subquery in subqueries {
552 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
553 }
554 changed
555 }
556 }
557}
558
559fn rewrite_query_source_routine_identity(
560 plan: &mut QueryPlan,
561 target: &FunctionBinding,
562 new_name: &str,
563) -> bool {
564 let mut changed = false;
565 for cte in &mut plan.ctes {
566 changed |= rewrite_cte_source_routine_identity(&mut cte.body, target, new_name);
567 }
568 changed | rewrite_relational_plan_source_routine_identity(&mut plan.root, target, new_name)
569}
570
571pub fn rewrite_query_plan_routine_identity(
572 plan: &mut QueryPlan,
573 target: &FunctionBinding,
574 new_name: &str,
575) -> bool {
576 let mut changed = false;
577 plan.rewrite_scalar_expressions(&mut |expression| {
578 let ScalarExpr::Func {
579 name,
580 binding: Some(binding),
581 ..
582 } = expression
583 else {
584 return;
585 };
586 if function_binding_matches(binding, target) {
587 *name = new_name.to_string();
588 binding.name = new_name.to_string();
589 changed = true;
590 }
591 });
592 changed | rewrite_query_source_routine_identity(plan, target, new_name)
593}
594
595pub fn bind_cte_plan_relations<E>(
596 body: &mut crate::plan::CtePlanBody,
597 inherited: &std::collections::BTreeSet<String>,
598 resolve: &mut impl FnMut(&str) -> Result<String, E>,
599) -> Result<(), E> {
600 bind_cte_relation_targets(body, inherited, &mut |name, _| resolve(name))
601}
602
603fn bind_cte_relation_targets<E>(
604 body: &mut crate::plan::CtePlanBody,
605 inherited: &std::collections::BTreeSet<String>,
606 resolve: &mut impl FnMut(&str, bool) -> Result<String, E>,
607) -> Result<(), E> {
608 let crate::plan::CtePlanBody::Command(command) = body else {
609 let crate::plan::CtePlanBody::Query(query) = body else {
610 unreachable!()
611 };
612 return bind_query_plan_relation_targets(query, inherited, resolve);
613 };
614 let include_descendants = match command.as_ref() {
615 crate::plan::CommandPlan::Update(plan) => plan.include_descendants,
616 crate::plan::CommandPlan::Delete(plan) => plan.include_descendants,
617 _ => false,
618 };
619 if let Some(target) = command.mutation_target_mut() {
620 *target = resolve(target, include_descendants)?;
621 }
622 match command.as_mut() {
623 crate::plan::CommandPlan::Insert(plan) => {
624 plan.target_relation_bound = true;
625 plan.relations_bound = true;
626 }
627 crate::plan::CommandPlan::Update(plan) => {
628 plan.target_relation_bound = true;
629 plan.relations_bound = true;
630 }
631 crate::plan::CommandPlan::Delete(plan) => {
632 plan.target_relation_bound = true;
633 plan.relations_bound = true;
634 }
635 _ => {}
636 }
637 let mut visible = inherited.clone();
638 if let Some(ctes) = command.ctes_mut() {
639 let recursive = ctes.iter().any(|cte| cte.recursive).then(|| {
640 ctes.iter()
641 .map(|cte| cte.name.clone())
642 .collect::<std::collections::BTreeSet<_>>()
643 });
644 for cte in ctes {
645 let scope = recursive.as_ref().map_or_else(
646 || visible.clone(),
647 |names| inherited.union(names).cloned().collect(),
648 );
649 bind_cte_relation_targets(&mut cte.body, &scope, resolve)?;
650 visible.insert(cte.name.clone());
651 }
652 }
653 if let Some(source) = command.source_input_mut() {
654 bind_source_targets(source, &visible, resolve)?;
655 }
656 for query in command.query_inputs_mut() {
657 bind_query_plan_relation_targets(query, &visible, resolve)?;
658 }
659 Ok(())
660}
661
662fn cte_plan_references_relation(
663 body: &crate::plan::CtePlanBody,
664 target: &RelationIdentity,
665 inherited: &std::collections::BTreeSet<String>,
666) -> bool {
667 match body {
668 crate::plan::CtePlanBody::Query(query) => {
669 query_plan_references_relation(query, target, inherited)
670 }
671 crate::plan::CtePlanBody::Command(command) => {
672 let mut visible = inherited.clone();
673 visible.extend(command.ctes().iter().map(|cte| cte.name.clone()));
674 command
675 .mutation_target()
676 .is_some_and(|name| relation_reference_matches(name, target))
677 || command
678 .ctes()
679 .iter()
680 .any(|cte| cte_plan_references_relation(&cte.body, target, &visible))
681 || command
682 .source_input()
683 .is_some_and(|source| source_plan_references_relation(source, target, &visible))
684 || command
685 .query_inputs()
686 .iter()
687 .any(|query| query_plan_references_relation(query, target, &visible))
688 }
689 }
690}
691
692fn cte_relational_inputs_any(
693 body: &crate::plan::CtePlanBody,
694 query: &dyn Fn(&QueryPlan) -> bool,
695 source: &dyn Fn(&crate::plan::SourcePlan) -> bool,
696) -> bool {
697 match body {
698 crate::plan::CtePlanBody::Query(plan) => query(plan),
699 crate::plan::CtePlanBody::Command(command) => {
700 command
701 .ctes()
702 .iter()
703 .any(|cte| cte_relational_inputs_any(&cte.body, query, source))
704 || command.query_inputs().iter().any(|plan| query(plan))
705 || command.source_input().is_some_and(source)
706 }
707 }
708}
709
710fn rewrite_cte_source_routine_identity(
711 body: &mut crate::plan::CtePlanBody,
712 target: &FunctionBinding,
713 new_name: &str,
714) -> bool {
715 match body {
716 crate::plan::CtePlanBody::Query(query) => {
717 rewrite_query_source_routine_identity(query, target, new_name)
718 }
719 crate::plan::CtePlanBody::Command(command) => {
720 let mut changed = false;
721 if let Some(ctes) = command.ctes_mut() {
722 for cte in ctes {
723 changed |= rewrite_cte_source_routine_identity(&mut cte.body, target, new_name);
724 }
725 }
726 if let Some(source) = command.source_input_mut() {
727 changed |= rewrite_source_plan_routine_identity(source, target, new_name);
728 }
729 for query in command.query_inputs_mut() {
730 changed |= rewrite_query_source_routine_identity(query, target, new_name);
731 }
732 changed
733 }
734 }
735}
736
737pub mod restoration;