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 crate::ColumnType::from_sql_name(ty)
40 .is_ok_and(|ty| matches!(ty, crate::ColumnType::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 = RelationIdentity::parse_reference(name).is_ok_and(|(schema, relation)| {
171 schema.is_none() && visible_ctes.contains(&relation)
172 });
173 if !is_cte {
174 *name = resolve(name, *include_descendants)?;
175 }
176 }
177 SourcePlan::Join { left, right, .. } => {
178 bind_source_targets(left, visible_ctes, resolve)?;
179 bind_source_targets(right, visible_ctes, resolve)?;
180 }
181 SourcePlan::Subquery { body, .. } => {
182 bind_query_plan_relation_targets(body, visible_ctes, resolve)?;
183 }
184 SourcePlan::Function {
185 name,
186 output_name,
187 relations,
188 ..
189 } => {
190 if output_name.is_empty() {
191 *output_name = RelationIdentity::parse_reference(name)
192 .map_or_else(|_| name.clone(), |(_, function)| function);
193 }
194 if let Some(relations) = relations {
195 relations.left = resolve(&relations.left, false)?;
196 relations.right = resolve(&relations.right, false)?;
197 }
198 }
199 SourcePlan::FunctionGroup { functions, .. } => {
200 for function in functions {
201 if function.output_name.is_empty() {
202 function.output_name = RelationIdentity::parse_reference(&function.name)
203 .map_or_else(|_| function.name.clone(), |(_, name)| name);
204 }
205 if let Some(relations) = &mut function.relations {
206 relations.left = resolve(&relations.left, false)?;
207 relations.right = resolve(&relations.right, false)?;
208 }
209 }
210 }
211 SourcePlan::Values { .. } => {}
212 }
213 Ok(())
214}
215
216pub fn relation_reference_matches(reference: &str, target: &RelationIdentity) -> bool {
217 match RelationIdentity::parse_reference(reference) {
218 Ok((Some(schema), name)) => schema == target.schema && name == target.name,
219 Ok((None, name)) => name == target.name,
220 Err(_) => true,
223 }
224}
225
226pub fn source_plan_references_relation(
227 source: &crate::plan::SourcePlan,
228 target: &RelationIdentity,
229 ctes: &std::collections::BTreeSet<String>,
230) -> bool {
231 match source {
232 crate::plan::SourcePlan::Table { name, .. } => {
233 let is_cte = RelationIdentity::parse_reference(name)
234 .is_ok_and(|(schema, relation)| schema.is_none() && ctes.contains(&relation));
235 !is_cte && relation_reference_matches(name, target)
236 }
237 crate::plan::SourcePlan::Join { left, right, .. } => {
238 source_plan_references_relation(left, target, ctes)
239 || source_plan_references_relation(right, target, ctes)
240 }
241 crate::plan::SourcePlan::Subquery { body, .. } => {
242 query_plan_references_relation(body, target, ctes)
243 }
244 crate::plan::SourcePlan::Function { relations, .. } => {
245 relations.as_ref().is_some_and(|relations| {
246 relation_reference_matches(&relations.left, target)
247 || relation_reference_matches(&relations.right, target)
248 })
249 }
250 crate::plan::SourcePlan::FunctionGroup { functions, .. } => {
251 functions.iter().any(|function| {
252 function.relations.as_ref().is_some_and(|relations| {
253 relation_reference_matches(&relations.left, target)
254 || relation_reference_matches(&relations.right, target)
255 })
256 })
257 }
258 crate::plan::SourcePlan::Values { .. } => false,
259 }
260}
261
262pub fn query_plan_references_relation(
263 query: &crate::plan::QueryPlan,
264 target: &RelationIdentity,
265 inherited_ctes: &std::collections::BTreeSet<String>,
266) -> bool {
267 let mut ctes = inherited_ctes.clone();
268 ctes.extend(query.ctes.iter().map(|cte| cte.name.clone()));
269 if query
270 .ctes
271 .iter()
272 .any(|cte| cte_plan_references_relation(&cte.body, target, &ctes))
273 {
274 return true;
275 }
276 match &query.root {
277 crate::plan::RelationalPlan::QueryBlock(block) => {
278 block
279 .from
280 .as_ref()
281 .is_some_and(|source| source_plan_references_relation(source, target, &ctes))
282 || block
283 .subqueries
284 .iter()
285 .any(|query| query_plan_references_relation(query, target, &ctes))
286 }
287 crate::plan::RelationalPlan::SetOp {
288 left,
289 right,
290 subqueries,
291 ..
292 } => {
293 query_plan_references_relation(left, target, &ctes)
294 || query_plan_references_relation(right, target, &ctes)
295 || subqueries
296 .iter()
297 .any(|query| query_plan_references_relation(query, target, &ctes))
298 }
299 crate::plan::RelationalPlan::Values { subqueries, .. } => subqueries
300 .iter()
301 .any(|query| query_plan_references_relation(query, target, &ctes)),
302 }
303}
304
305pub fn query_plan_references_sequence(plan: &QueryPlan, target: &RelationIdentity) -> bool {
306 let mut plan = plan.clone();
307 let mut referenced = false;
308 plan.rewrite_scalar_expressions(&mut |expression| {
309 if let Some(reference) = sequence_function_reference_mut(expression) {
310 referenced |= relation_reference_matches(reference, target);
311 }
312 });
313 referenced
314}
315
316pub use crate::routines::function_binding_matches;
317
318fn function_binding_needs_object_identity(binding: &FunctionBinding) -> bool {
319 !binding.builtin
320 && binding.dispatch.is_none()
321 && binding.resolution_error.is_none()
322 && binding.object_id.is_none()
323}
324
325fn source_plan_has_legacy_routine_identity(source: &SourcePlan) -> bool {
326 match source {
327 SourcePlan::Table { .. } | SourcePlan::Values { .. } => false,
328 SourcePlan::Join { left, right, .. } => {
329 source_plan_has_legacy_routine_identity(left)
330 || source_plan_has_legacy_routine_identity(right)
331 }
332 SourcePlan::Subquery { body, .. } => query_plan_sources_have_legacy_routine_identity(body),
333 SourcePlan::Function { binding, .. } => binding
334 .as_ref()
335 .is_some_and(function_binding_needs_object_identity),
336 SourcePlan::FunctionGroup { functions, .. } => functions.iter().any(|function| {
337 function
338 .binding
339 .as_ref()
340 .is_some_and(function_binding_needs_object_identity)
341 }),
342 }
343}
344
345fn relational_plan_has_legacy_routine_identity(plan: &RelationalPlan) -> bool {
346 match plan {
347 RelationalPlan::QueryBlock(block) => {
348 block
349 .from
350 .as_ref()
351 .is_some_and(source_plan_has_legacy_routine_identity)
352 || block
353 .subqueries
354 .iter()
355 .any(query_plan_sources_have_legacy_routine_identity)
356 }
357 RelationalPlan::SetOp {
358 left,
359 right,
360 subqueries,
361 ..
362 } => {
363 query_plan_sources_have_legacy_routine_identity(left)
364 || query_plan_sources_have_legacy_routine_identity(right)
365 || subqueries
366 .iter()
367 .any(query_plan_sources_have_legacy_routine_identity)
368 }
369 RelationalPlan::Values { subqueries, .. } => subqueries
370 .iter()
371 .any(query_plan_sources_have_legacy_routine_identity),
372 }
373}
374
375fn query_plan_sources_have_legacy_routine_identity(plan: &QueryPlan) -> bool {
376 plan.ctes.iter().any(|cte| {
377 cte_relational_inputs_any(
378 &cte.body,
379 &query_plan_sources_have_legacy_routine_identity,
380 &source_plan_has_legacy_routine_identity,
381 )
382 }) || relational_plan_has_legacy_routine_identity(&plan.root)
383}
384
385pub fn query_plan_has_legacy_routine_identity(plan: &QueryPlan) -> bool {
386 query_plan_has_legacy_routine_identity_with_types(plan, None)
387}
388
389pub fn query_plan_has_legacy_routine_identity_with_types(
391 plan: &QueryPlan,
392 types: Option<&dyn crate::FunctionTypeResolver>,
393) -> bool {
394 let mut scalar_plan = plan.clone();
395 let mut legacy = false;
396 scalar_plan.rewrite_scalar_expressions(&mut |expression| {
397 if let ScalarExpr::Func {
398 name,
399 binding,
400 order_syntax,
401 order_by,
402 ..
403 } = expression
404 {
405 legacy |= binding
406 .as_ref()
407 .is_some_and(function_binding_needs_object_identity);
408 legacy |=
409 crate::type_resolution::json_path::requires_binding(name, binding.as_ref(), types);
410 legacy |= crate::type_resolution::concatenation::requires_binding(
411 name,
412 binding.as_ref(),
413 types,
414 );
415 legacy |= *order_syntax == crate::ast::FunctionOrderSyntax::Legacy
418 && super::ordered_calls::uses_ordered_arguments(
419 *order_syntax,
420 name,
421 binding.as_ref(),
422 order_by.len(),
423 );
424 }
425 });
426 legacy || query_plan_sources_have_legacy_routine_identity(plan)
427}
428
429fn source_plan_references_function(source: &SourcePlan, target: &FunctionBinding) -> bool {
430 match source {
431 SourcePlan::Table { .. } | SourcePlan::Values { .. } => false,
432 SourcePlan::Join { left, right, .. } => {
433 source_plan_references_function(left, target)
434 || source_plan_references_function(right, target)
435 }
436 SourcePlan::Subquery { body, .. } => query_plan_sources_reference_function(body, target),
437 SourcePlan::Function { binding, .. } => binding
438 .as_ref()
439 .is_some_and(|binding| function_binding_matches(binding, target)),
440 SourcePlan::FunctionGroup { functions, .. } => functions.iter().any(|function| {
441 function
442 .binding
443 .as_ref()
444 .is_some_and(|binding| function_binding_matches(binding, target))
445 }),
446 }
447}
448
449fn relational_plan_references_function(plan: &RelationalPlan, target: &FunctionBinding) -> bool {
450 match plan {
451 RelationalPlan::QueryBlock(block) => {
452 block
453 .from
454 .as_ref()
455 .is_some_and(|source| source_plan_references_function(source, target))
456 || block
457 .subqueries
458 .iter()
459 .any(|query| query_plan_sources_reference_function(query, target))
460 }
461 RelationalPlan::SetOp {
462 left,
463 right,
464 subqueries,
465 ..
466 } => {
467 query_plan_sources_reference_function(left, target)
468 || query_plan_sources_reference_function(right, target)
469 || subqueries
470 .iter()
471 .any(|query| query_plan_sources_reference_function(query, target))
472 }
473 RelationalPlan::Values { subqueries, .. } => subqueries
474 .iter()
475 .any(|query| query_plan_sources_reference_function(query, target)),
476 }
477}
478
479fn query_plan_sources_reference_function(plan: &QueryPlan, target: &FunctionBinding) -> bool {
480 plan.ctes.iter().any(|cte| {
481 cte_relational_inputs_any(
482 &cte.body,
483 &|query| query_plan_sources_reference_function(query, target),
484 &|source| source_plan_references_function(source, target),
485 )
486 }) || relational_plan_references_function(&plan.root, target)
487}
488
489pub fn query_plan_references_function(plan: &QueryPlan, target: &FunctionBinding) -> bool {
490 let mut scalar_plan = plan.clone();
491 let mut referenced = false;
492 scalar_plan.rewrite_scalar_expressions(&mut |expression| {
493 if let ScalarExpr::Func {
494 binding: Some(binding),
495 ..
496 } = expression
497 {
498 referenced |= function_binding_matches(binding, target);
499 }
500 });
501 referenced || query_plan_sources_reference_function(plan, target)
502}
503
504fn rewrite_source_plan_routine_identity(
505 source: &mut SourcePlan,
506 target: &FunctionBinding,
507 new_name: &str,
508) -> bool {
509 match source {
510 SourcePlan::Table { .. } | SourcePlan::Values { .. } => false,
511 SourcePlan::Join { left, right, .. } => {
512 rewrite_source_plan_routine_identity(left, target, new_name)
513 | rewrite_source_plan_routine_identity(right, target, new_name)
514 }
515 SourcePlan::Subquery { body, .. } => {
516 rewrite_query_source_routine_identity(body, target, new_name)
517 }
518 SourcePlan::Function { name, binding, .. } => {
519 let Some(binding) = binding.as_mut() else {
520 return false;
521 };
522 if !function_binding_matches(binding, target) {
523 return false;
524 }
525 *name = new_name.to_string();
526 binding.name = new_name.to_string();
527 true
528 }
529 SourcePlan::FunctionGroup { functions, .. } => {
530 let mut changed = false;
531 for function in functions {
532 let Some(binding) = function.binding.as_mut() else {
533 continue;
534 };
535 if function_binding_matches(binding, target) {
536 function.name = new_name.to_string();
537 binding.name = new_name.to_string();
538 changed = true;
539 }
540 }
541 changed
542 }
543 }
544}
545
546fn rewrite_relational_plan_source_routine_identity(
547 plan: &mut RelationalPlan,
548 target: &FunctionBinding,
549 new_name: &str,
550) -> bool {
551 match plan {
552 RelationalPlan::QueryBlock(block) => {
553 let mut changed = block.from.as_mut().is_some_and(|source| {
554 rewrite_source_plan_routine_identity(source, target, new_name)
555 });
556 for subquery in &mut block.subqueries {
557 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
558 }
559 changed
560 }
561 RelationalPlan::SetOp {
562 left,
563 right,
564 subqueries,
565 ..
566 } => {
567 let mut changed = rewrite_query_source_routine_identity(left, target, new_name)
568 | rewrite_query_source_routine_identity(right, target, new_name);
569 for subquery in subqueries {
570 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
571 }
572 changed
573 }
574 RelationalPlan::Values { subqueries, .. } => {
575 let mut changed = false;
576 for subquery in subqueries {
577 changed |= rewrite_query_source_routine_identity(subquery, target, new_name);
578 }
579 changed
580 }
581 }
582}
583
584fn rewrite_query_source_routine_identity(
585 plan: &mut QueryPlan,
586 target: &FunctionBinding,
587 new_name: &str,
588) -> bool {
589 let mut changed = false;
590 for cte in &mut plan.ctes {
591 changed |= rewrite_cte_source_routine_identity(&mut cte.body, target, new_name);
592 }
593 changed | rewrite_relational_plan_source_routine_identity(&mut plan.root, target, new_name)
594}
595
596pub fn rewrite_query_plan_routine_identity(
597 plan: &mut QueryPlan,
598 target: &FunctionBinding,
599 new_name: &str,
600) -> bool {
601 let mut changed = false;
602 plan.rewrite_scalar_expressions(&mut |expression| {
603 let ScalarExpr::Func {
604 name,
605 binding: Some(binding),
606 ..
607 } = expression
608 else {
609 return;
610 };
611 if function_binding_matches(binding, target) {
612 *name = new_name.to_string();
613 binding.name = new_name.to_string();
614 changed = true;
615 }
616 });
617 changed | rewrite_query_source_routine_identity(plan, target, new_name)
618}
619
620pub fn bind_cte_plan_relations<E>(
621 body: &mut crate::plan::CtePlanBody,
622 inherited: &std::collections::BTreeSet<String>,
623 resolve: &mut impl FnMut(&str) -> Result<String, E>,
624) -> Result<(), E> {
625 bind_cte_relation_targets(body, inherited, &mut |name, _| resolve(name))
626}
627
628fn bind_cte_relation_targets<E>(
629 body: &mut crate::plan::CtePlanBody,
630 inherited: &std::collections::BTreeSet<String>,
631 resolve: &mut impl FnMut(&str, bool) -> Result<String, E>,
632) -> Result<(), E> {
633 let crate::plan::CtePlanBody::Command(command) = body else {
634 let crate::plan::CtePlanBody::Query(query) = body else {
635 unreachable!()
636 };
637 return bind_query_plan_relation_targets(query, inherited, resolve);
638 };
639 let include_descendants = match command.as_ref() {
640 crate::plan::CommandPlan::Update(plan) => plan.include_descendants,
641 crate::plan::CommandPlan::Delete(plan) => plan.include_descendants,
642 _ => false,
643 };
644 if let Some(target) = command.mutation_target_mut() {
645 *target = resolve(target, include_descendants)?;
646 }
647 match command.as_mut() {
648 crate::plan::CommandPlan::Insert(plan) => {
649 plan.target_relation_bound = true;
650 plan.relations_bound = true;
651 }
652 crate::plan::CommandPlan::Update(plan) => {
653 plan.target_relation_bound = true;
654 plan.relations_bound = true;
655 }
656 crate::plan::CommandPlan::Delete(plan) => {
657 plan.target_relation_bound = true;
658 plan.relations_bound = true;
659 }
660 _ => {}
661 }
662 let mut visible = inherited.clone();
663 if let Some(ctes) = command.ctes_mut() {
664 let recursive = ctes.iter().any(|cte| cte.recursive).then(|| {
665 ctes.iter()
666 .map(|cte| cte.name.clone())
667 .collect::<std::collections::BTreeSet<_>>()
668 });
669 for cte in ctes {
670 let scope = recursive.as_ref().map_or_else(
671 || visible.clone(),
672 |names| inherited.union(names).cloned().collect(),
673 );
674 bind_cte_relation_targets(&mut cte.body, &scope, resolve)?;
675 visible.insert(cte.name.clone());
676 }
677 }
678 if let Some(source) = command.source_input_mut() {
679 bind_source_targets(source, &visible, resolve)?;
680 }
681 for query in command.query_inputs_mut() {
682 bind_query_plan_relation_targets(query, &visible, resolve)?;
683 }
684 Ok(())
685}
686
687fn cte_plan_references_relation(
688 body: &crate::plan::CtePlanBody,
689 target: &RelationIdentity,
690 inherited: &std::collections::BTreeSet<String>,
691) -> bool {
692 match body {
693 crate::plan::CtePlanBody::Query(query) => {
694 query_plan_references_relation(query, target, inherited)
695 }
696 crate::plan::CtePlanBody::Command(command) => {
697 let mut visible = inherited.clone();
698 visible.extend(command.ctes().iter().map(|cte| cte.name.clone()));
699 command
700 .mutation_target()
701 .is_some_and(|name| relation_reference_matches(name, target))
702 || command
703 .ctes()
704 .iter()
705 .any(|cte| cte_plan_references_relation(&cte.body, target, &visible))
706 || command
707 .source_input()
708 .is_some_and(|source| source_plan_references_relation(source, target, &visible))
709 || command
710 .query_inputs()
711 .iter()
712 .any(|query| query_plan_references_relation(query, target, &visible))
713 }
714 }
715}
716
717fn cte_relational_inputs_any(
718 body: &crate::plan::CtePlanBody,
719 query: &dyn Fn(&QueryPlan) -> bool,
720 source: &dyn Fn(&crate::plan::SourcePlan) -> bool,
721) -> bool {
722 match body {
723 crate::plan::CtePlanBody::Query(plan) => query(plan),
724 crate::plan::CtePlanBody::Command(command) => {
725 command
726 .ctes()
727 .iter()
728 .any(|cte| cte_relational_inputs_any(&cte.body, query, source))
729 || command.query_inputs().iter().any(|plan| query(plan))
730 || command.source_input().is_some_and(source)
731 }
732 }
733}
734
735fn rewrite_cte_source_routine_identity(
736 body: &mut crate::plan::CtePlanBody,
737 target: &FunctionBinding,
738 new_name: &str,
739) -> bool {
740 match body {
741 crate::plan::CtePlanBody::Query(query) => {
742 rewrite_query_source_routine_identity(query, target, new_name)
743 }
744 crate::plan::CtePlanBody::Command(command) => {
745 let mut changed = false;
746 if let Some(ctes) = command.ctes_mut() {
747 for cte in ctes {
748 changed |= rewrite_cte_source_routine_identity(&mut cte.body, target, new_name);
749 }
750 }
751 if let Some(source) = command.source_input_mut() {
752 changed |= rewrite_source_plan_routine_identity(source, target, new_name);
753 }
754 for query in command.query_inputs_mut() {
755 changed |= rewrite_query_source_routine_identity(query, target, new_name);
756 }
757 changed
758 }
759 }
760}
761
762pub mod restoration;