1use crate::ast::FunctionBinding;
10use crate::plan::{
11 AccessPathPlan, CommandPlan, ComputePlan, ConflictActionPlan, CtePlan, DeletePlan, InsertPlan,
12 JoinExecutionStrategy, MergePlan, ProjectionPlan, QueryBlockPlan, QueryPlan, RelationalPlan,
13 SourcePlan, UnifiedPlan, UpdatePlan,
14};
15use crate::SQLError;
16use crate::{RowSchema, ScalarExpr, ScalarFrameBound};
17
18use crate::{binding::context::BindingContext, routines::RoutineResolution};
19use uqa_core::Value;
20
21pub struct CatalogRoutineContext<'a, 'q> {
23 pub routines: &'a dyn RoutineResolution,
24 pub binding: &'a BindingContext<'q>,
25}
26
27pub struct BoundStatementRoutines {
28 pub query: Option<QueryPlan>,
29 pub references: Vec<BoundRoutineReference>,
30}
31
32#[derive(Debug, Clone)]
33pub struct BoundRoutineReference {
34 pub name: String,
35 pub binding: Option<FunctionBinding>,
36}
37
38struct CommandRoutineInputs {
39 ctes: Vec<CtePlan>,
40 source: Option<SourcePlan>,
41 expressions: Vec<ScalarExpr>,
42 subqueries: Vec<QueryPlan>,
43 outer: RowSchema,
44}
45
46pub fn bind_catalog_statement_routines(
47 context: &CatalogRoutineContext<'_, '_>,
48 plan: &UnifiedPlan,
49) -> Result<BoundStatementRoutines, SQLError> {
50 let query = match plan {
51 UnifiedPlan::Query(query) => {
52 let mut query = (**query).clone();
53 mark_query_relations_bound(&mut query);
54 crate::binding::bind_query_plan_routines_for_storage(
55 context.routines,
56 &mut query,
57 &[],
58 context.binding,
59 None,
60 )?;
61 Some(query)
62 }
63 UnifiedPlan::Command(command) => bind_command_statement_routines(context, command)?,
64 };
65 let mut references = Vec::new();
66 if let Some(query) = &query {
67 collect_query_routine_references(query, &mut references)?;
68 }
69 Ok(BoundStatementRoutines { query, references })
70}
71
72pub fn mark_catalog_statement_relations_bound(plan: &mut UnifiedPlan) -> Result<(), SQLError> {
73 match plan {
74 UnifiedPlan::Query(query) => mark_query_relations_bound(query),
75 UnifiedPlan::Command(command) => match command.as_mut() {
76 CommandPlan::Insert(plan) => {
77 plan.relations_bound = true;
78 for cte in &mut plan.ctes {
79 mark_cte_relations_bound(&mut cte.body);
80 }
81 if let Some(source) = &mut plan.source {
82 mark_query_relations_bound(source);
83 }
84 for subquery in &mut plan.subqueries {
85 mark_query_relations_bound(subquery);
86 }
87 }
88 CommandPlan::Update(plan) => {
89 plan.relations_bound = true;
90 for cte in &mut plan.ctes {
91 mark_cte_relations_bound(&mut cte.body);
92 }
93 if let Some(source) = &mut plan.source {
94 mark_source_relations_bound(source);
95 }
96 for subquery in &mut plan.subqueries {
97 mark_query_relations_bound(subquery);
98 }
99 }
100 CommandPlan::Delete(plan) => {
101 plan.relations_bound = true;
102 for cte in &mut plan.ctes {
103 mark_cte_relations_bound(&mut cte.body);
104 }
105 if let Some(source) = &mut plan.source {
106 mark_source_relations_bound(source);
107 }
108 for subquery in &mut plan.subqueries {
109 mark_query_relations_bound(subquery);
110 }
111 }
112 CommandPlan::Notify { .. } => {}
113 CommandPlan::Merge(plan) => {
114 for cte in &mut plan.ctes {
115 mark_cte_relations_bound(&mut cte.body);
116 }
117 mark_source_relations_bound(&mut plan.source);
118 for subquery in &mut plan.subqueries {
119 mark_query_relations_bound(subquery);
120 }
121 }
122 _ => {
123 return Err(SQLError::Internal(
124 "catalog-owned statement lowered to an unsupported command".into(),
125 ));
126 }
127 },
128 }
129 Ok(())
130}
131
132fn bind_command_statement_routines(
133 context: &CatalogRoutineContext<'_, '_>,
134 command: &CommandPlan,
135) -> Result<Option<QueryPlan>, SQLError> {
136 let Some(inputs) = command_statement_routine_inputs(context, command)? else {
137 return Ok(None);
138 };
139 let projections = inputs
140 .expressions
141 .into_iter()
142 .filter(|expression| expression_contains_routine(expression, &inputs.subqueries))
143 .filter(|expression| !matches!(expression, ScalarExpr::Default))
144 .map(|expr| ProjectionPlan { expr, alias: None })
145 .chain(std::iter::once(ProjectionPlan {
146 expr: ScalarExpr::Literal(Value::Int(1)),
147 alias: None,
148 }))
149 .collect();
150 let mut query = QueryPlan {
151 relations_bound: true,
152 ctes: inputs.ctes,
153 root: RelationalPlan::QueryBlock(Box::new(QueryBlockPlan {
154 projections,
155 from: inputs.source,
156 r#where: None,
157 compute: ComputePlan::Project,
158 group_by: Vec::new(),
159 grouping_sets: Vec::new(),
160 group_distinct: false,
161 having: None,
162 order_by: Vec::new(),
163 limit: None,
164 with_ties: false,
165 offset: None,
166 distinct: false,
167 distinct_on: Vec::new(),
168 subqueries: inputs.subqueries,
169 access: AccessPathPlan::Row,
170 locking: Vec::new(),
171 })),
172 };
173 mark_query_relations_bound(&mut query);
174 crate::binding::bind_query_plan_routines_for_storage(
175 context.routines,
176 &mut query,
177 &[],
178 context.binding,
179 Some(&inputs.outer),
180 )?;
181 Ok(Some(query))
182}
183
184fn command_statement_routine_inputs(
185 context: &CatalogRoutineContext<'_, '_>,
186 command: &CommandPlan,
187) -> Result<Option<CommandRoutineInputs>, SQLError> {
188 match command {
189 CommandPlan::Insert(plan) => insert_statement_routine_inputs(context, plan).map(Some),
190 CommandPlan::Update(plan) => update_statement_routine_inputs(context, plan).map(Some),
191 CommandPlan::Delete(plan) => delete_statement_routine_inputs(context, plan).map(Some),
192 CommandPlan::Merge(plan) => Ok(Some(merge_statement_routine_inputs(plan))),
193 CommandPlan::Notify { .. } => Ok(None),
194 _ => Err(SQLError::Internal(
195 "catalog-owned statement lowered to an unsupported command".into(),
196 )),
197 }
198}
199
200fn merge_statement_routine_inputs(plan: &MergePlan) -> CommandRoutineInputs {
201 let target = SourcePlan::Table {
202 bound_columns: None,
203 name: plan.target.clone(),
204 qualifier: plan.target_qualifier.clone(),
205 alias: plan.target_alias.clone(),
206 column_aliases: Vec::new(),
207 include_descendants: plan.include_descendants,
208 };
209 let source = SourcePlan::Join {
210 left: Box::new(target),
211 right: plan.source.clone(),
212 kind: crate::ast::JoinKind::Cross,
213 on: None,
214 using: None,
215 natural: false,
216 alias: None,
217 column_aliases: Vec::new(),
218 lateral: false,
219 strategy: JoinExecutionStrategy::default(),
220 };
221 let mut expressions = vec![plan.join_condition.clone()];
222 for clause in &plan.when_clauses {
223 match clause {
224 crate::plan::MergeWhenPlan::UpdateMatched {
225 condition,
226 assignments,
227 }
228 | crate::plan::MergeWhenPlan::UpdateNotMatchedBySource {
229 condition,
230 assignments,
231 } => {
232 expressions.extend(condition.iter().cloned());
233 expressions.extend(
234 assignments
235 .iter()
236 .flat_map(crate::plan::AssignmentPlan::expressions)
237 .cloned(),
238 );
239 }
240 crate::plan::MergeWhenPlan::InsertNotMatched {
241 condition,
242 columns,
243 values,
244 ..
245 } => {
246 expressions.extend(condition.iter().cloned());
247 expressions.extend(
248 columns
249 .iter()
250 .flat_map(crate::ast::AssignmentTarget::expressions)
251 .cloned(),
252 );
253 expressions.extend(values.iter().cloned());
254 }
255 crate::plan::MergeWhenPlan::DeleteMatched { condition }
256 | crate::plan::MergeWhenPlan::DeleteNotMatchedBySource { condition }
257 | crate::plan::MergeWhenPlan::NothingMatched { condition }
258 | crate::plan::MergeWhenPlan::NothingNotMatched { condition }
259 | crate::plan::MergeWhenPlan::NothingNotMatchedBySource { condition } => {
260 expressions.extend(condition.iter().cloned());
261 }
262 }
263 }
264 expressions.extend(
265 plan.returning
266 .iter()
267 .map(|projection| projection.expr.clone()),
268 );
269 CommandRoutineInputs {
270 ctes: plan.ctes.clone(),
271 source: Some(source),
272 expressions,
273 subqueries: plan.subqueries.clone(),
274 outer: RowSchema::default(),
275 }
276}
277
278fn insert_statement_routine_inputs(
279 context: &CatalogRoutineContext<'_, '_>,
280 plan: &InsertPlan,
281) -> Result<CommandRoutineInputs, SQLError> {
282 let mut expressions = plan
283 .columns
284 .iter()
285 .flat_map(crate::ast::AssignmentTarget::expressions)
286 .chain(plan.rows.iter().flatten())
287 .cloned()
288 .collect::<Vec<_>>();
289 if let Some(conflict) = &plan.on_conflict {
290 expressions.extend(conflict.expressions.iter().cloned());
291 expressions.extend(conflict.predicate.iter().map(Box::as_ref).cloned());
292 if let ConflictActionPlan::Update {
293 assignments,
294 predicate,
295 } = &conflict.action
296 {
297 expressions.extend(
298 assignments
299 .iter()
300 .flat_map(crate::plan::AssignmentPlan::expressions)
301 .cloned(),
302 );
303 expressions.extend(predicate.iter().map(Box::as_ref).cloned());
304 }
305 }
306 expressions.extend(
307 plan.returning
308 .iter()
309 .map(|projection| projection.expr.clone()),
310 );
311 let source = plan.source.as_ref().map(|source| SourcePlan::Subquery {
312 body: Box::new((**source).clone()),
313 alias: Some("__uqa_catalog_statement_source".into()),
314 column_aliases: Vec::new(),
315 });
316 Ok(CommandRoutineInputs {
317 ctes: plan.ctes.clone(),
318 source,
319 expressions,
320 subqueries: plan.subqueries.clone(),
321 outer: statement_target_outer_schema(
322 context,
323 &plan.table,
324 &plan.target_qualifier,
325 &plan.returning_aliases,
326 )?,
327 })
328}
329
330fn update_statement_routine_inputs(
331 context: &CatalogRoutineContext<'_, '_>,
332 plan: &UpdatePlan,
333) -> Result<CommandRoutineInputs, SQLError> {
334 let mut expressions = plan
335 .assignments
336 .iter()
337 .flat_map(crate::plan::AssignmentPlan::expressions)
338 .cloned()
339 .collect::<Vec<_>>();
340 expressions.extend(plan.predicate.iter().cloned());
341 expressions.extend(
342 plan.returning
343 .iter()
344 .map(|projection| projection.expr.clone()),
345 );
346 Ok(CommandRoutineInputs {
347 ctes: plan.ctes.clone(),
348 source: plan.source.as_deref().cloned(),
349 expressions,
350 subqueries: plan.subqueries.clone(),
351 outer: statement_target_outer_schema(
352 context,
353 &plan.table,
354 &plan.target_qualifier,
355 &plan.returning_aliases,
356 )?,
357 })
358}
359
360fn delete_statement_routine_inputs(
361 context: &CatalogRoutineContext<'_, '_>,
362 plan: &DeletePlan,
363) -> Result<CommandRoutineInputs, SQLError> {
364 let mut expressions = plan.predicate.iter().cloned().collect::<Vec<_>>();
365 expressions.extend(
366 plan.returning
367 .iter()
368 .map(|projection| projection.expr.clone()),
369 );
370 Ok(CommandRoutineInputs {
371 ctes: plan.ctes.clone(),
372 source: plan.source.as_deref().cloned(),
373 expressions,
374 subqueries: plan.subqueries.clone(),
375 outer: statement_target_outer_schema(
376 context,
377 &plan.table,
378 &plan.target_qualifier,
379 &plan.returning_aliases,
380 )?,
381 })
382}
383
384fn expression_contains_routine(expression: &ScalarExpr, subqueries: &[QueryPlan]) -> bool {
385 let mut references = Vec::new();
386 collect_scalar_routine_references(expression, subqueries, &mut references).is_ok()
387 && !references.is_empty()
388}
389
390fn statement_target_outer_schema(
391 context: &CatalogRoutineContext<'_, '_>,
392 table: &str,
393 target_qualifier: &str,
394 aliases: &crate::ast::ReturningAliases,
395) -> Result<RowSchema, SQLError> {
396 let target = crate::binding::analyze_source_plan_schema(
397 context.routines,
398 &SourcePlan::Table {
399 bound_columns: None,
400 name: table.to_string(),
401 qualifier: target_qualifier.to_string(),
402 alias: None,
403 column_aliases: Vec::new(),
404 include_descendants: true,
405 },
406 &[],
407 context.binding,
408 None,
409 )?;
410 let target = RowSchema::with_types(target.columns().to_vec(), target.column_types().to_vec());
411 Ok(crate::semantics::returning_expression_schema(
412 &target,
413 target_qualifier,
414 aliases,
415 None,
416 ))
417}
418
419pub fn collect_expression_routine_references(
420 expression: &crate::plan::ExpressionPlan,
421) -> Result<Vec<BoundRoutineReference>, SQLError> {
422 let mut references = Vec::new();
423 collect_scalar_routine_references(&expression.scalar, &expression.subqueries, &mut references)?;
424 Ok(references)
425}
426
427fn collect_query_routine_references(
428 query: &QueryPlan,
429 references: &mut Vec<BoundRoutineReference>,
430) -> Result<(), SQLError> {
431 for cte in &query.ctes {
432 collect_cte_routine_references(&cte.body, references)?;
433 if let Some(cycle) = &cte.cycle {
434 collect_scalar_routine_references(&cycle.mark_value, &[], references)?;
435 collect_scalar_routine_references(&cycle.mark_default, &[], references)?;
436 }
437 }
438 match &query.root {
439 RelationalPlan::QueryBlock(block) => {
440 if let Some(source) = &block.from {
441 collect_source_routine_references(source, &block.subqueries, references)?;
442 }
443 for projection in &block.projections {
444 collect_scalar_routine_references(&projection.expr, &block.subqueries, references)?;
445 }
446 if let Some(expression) = &block.r#where {
447 collect_scalar_routine_references(expression, &block.subqueries, references)?;
448 }
449 for expression in &block.group_by {
450 collect_scalar_routine_references(expression, &block.subqueries, references)?;
451 }
452 for expression in block.grouping_sets.iter().flatten() {
453 collect_scalar_routine_references(expression, &block.subqueries, references)?;
454 }
455 if let Some(expression) = &block.having {
456 collect_scalar_routine_references(expression, &block.subqueries, references)?;
457 }
458 for order in &block.order_by {
459 collect_scalar_routine_references(&order.expr, &block.subqueries, references)?;
460 }
461 if let Some(expression) = &block.limit {
462 collect_scalar_routine_references(expression, &block.subqueries, references)?;
463 }
464 if let Some(expression) = &block.offset {
465 collect_scalar_routine_references(expression, &block.subqueries, references)?;
466 }
467 for expression in &block.distinct_on {
468 collect_scalar_routine_references(expression, &block.subqueries, references)?;
469 }
470 }
471 RelationalPlan::SetOp {
472 left,
473 right,
474 order_by,
475 limit,
476 offset,
477 subqueries,
478 ..
479 } => {
480 collect_query_routine_references(left, references)?;
481 collect_query_routine_references(right, references)?;
482 for order in order_by {
483 collect_scalar_routine_references(&order.expr, subqueries, references)?;
484 }
485 if let Some(expression) = limit {
486 collect_scalar_routine_references(expression, subqueries, references)?;
487 }
488 if let Some(expression) = offset {
489 collect_scalar_routine_references(expression, subqueries, references)?;
490 }
491 }
492 RelationalPlan::Values { rows, subqueries } => {
493 for expression in rows.iter().flatten() {
494 collect_scalar_routine_references(expression, subqueries, references)?;
495 }
496 }
497 }
498 Ok(())
499}
500
501fn collect_source_routine_references(
502 source: &SourcePlan,
503 subqueries: &[QueryPlan],
504 references: &mut Vec<BoundRoutineReference>,
505) -> Result<(), SQLError> {
506 match source {
507 SourcePlan::Table { .. } => {}
508 SourcePlan::Join {
509 left, right, on, ..
510 } => {
511 collect_source_routine_references(left, subqueries, references)?;
512 collect_source_routine_references(right, subqueries, references)?;
513 if let Some(expression) = on {
514 collect_scalar_routine_references(expression, subqueries, references)?;
515 }
516 }
517 SourcePlan::Values { rows, .. } => {
518 for expression in rows.iter().flatten() {
519 collect_scalar_routine_references(expression, subqueries, references)?;
520 }
521 }
522 SourcePlan::Function {
523 name,
524 binding,
525 args,
526 ..
527 } => {
528 references.push(BoundRoutineReference {
529 name: name.clone(),
530 binding: binding.clone(),
531 });
532 for expression in args {
533 collect_scalar_routine_references(expression, subqueries, references)?;
534 }
535 }
536 SourcePlan::FunctionGroup { functions, .. } => {
537 for function in functions {
538 references.push(BoundRoutineReference {
539 name: function.name.clone(),
540 binding: function.binding.clone(),
541 });
542 for expression in &function.args {
543 collect_scalar_routine_references(expression, subqueries, references)?;
544 }
545 }
546 }
547 SourcePlan::Subquery { body, .. } => {
548 collect_query_routine_references(body, references)?;
549 }
550 }
551 Ok(())
552}
553
554fn collect_scalar_routine_references(
555 expression: &ScalarExpr,
556 subqueries: &[QueryPlan],
557 references: &mut Vec<BoundRoutineReference>,
558) -> Result<(), SQLError> {
559 match expression {
560 ScalarExpr::Func {
561 name,
562 binding,
563 args,
564 order_by,
565 filter,
566 ..
567 } => {
568 for argument in args {
569 collect_scalar_routine_references(argument, subqueries, references)?;
570 }
571 for order in order_by {
572 collect_scalar_routine_references(&order.expr, subqueries, references)?;
573 }
574 if let Some(filter) = filter {
575 collect_scalar_routine_references(filter, subqueries, references)?;
576 }
577 references.push(BoundRoutineReference {
578 name: name.clone(),
579 binding: binding.clone(),
580 });
581 }
582 ScalarExpr::Array(items)
583 | ScalarExpr::Row(items)
584 | ScalarExpr::And(items)
585 | ScalarExpr::Or(items) => collect_many_routine_references(items, subqueries, references)?,
586 ScalarExpr::Binary { lhs, rhs, .. } => {
587 collect_scalar_routine_references(lhs, subqueries, references)?;
588 collect_scalar_routine_references(rhs, subqueries, references)?;
589 }
590 ScalarExpr::UnaryMinus(inner)
591 | ScalarExpr::Not(inner)
592 | ScalarExpr::IsNull { expr: inner, .. }
593 | ScalarExpr::Cast { expr: inner, .. } => {
594 collect_scalar_routine_references(inner, subqueries, references)?;
595 }
596 ScalarExpr::Between { expr, low, high } => {
597 collect_scalar_routine_references(expr, subqueries, references)?;
598 collect_scalar_routine_references(low, subqueries, references)?;
599 collect_scalar_routine_references(high, subqueries, references)?;
600 }
601 ScalarExpr::InList { expr, list, .. } => {
602 collect_scalar_routine_references(expr, subqueries, references)?;
603 for item in list {
604 collect_scalar_routine_references(item, subqueries, references)?;
605 }
606 }
607 ScalarExpr::WindowCall { name, args, spec } => {
608 collect_window_routine_references(name, args, spec, subqueries, references)?;
609 }
610 ScalarExpr::Case {
611 base,
612 when,
613 else_branch,
614 } => collect_case_routine_references(
615 base.as_deref(),
616 when,
617 else_branch.as_deref(),
618 subqueries,
619 references,
620 )?,
621 ScalarExpr::ScalarSubquery(index)
622 | ScalarExpr::Exists {
623 subquery: index, ..
624 } => {
625 let query = subqueries.get(*index).ok_or_else(|| {
626 SQLError::Internal(format!(
627 "stored catalog routine binding cannot resolve subquery slot {index}"
628 ))
629 })?;
630 collect_query_routine_references(query, references)?;
631 }
632 ScalarExpr::InSubquery {
633 expr,
634 subquery: index,
635 ..
636 } => {
637 collect_scalar_routine_references(expr, subqueries, references)?;
638 let query = subqueries.get(*index).ok_or_else(|| {
639 SQLError::Internal(format!(
640 "stored catalog routine binding cannot resolve subquery slot {index}"
641 ))
642 })?;
643 collect_query_routine_references(query, references)?;
644 }
645 ScalarExpr::Star
646 | ScalarExpr::QualifiedStar(_)
647 | ScalarExpr::Default
648 | ScalarExpr::Column(_)
649 | ScalarExpr::Position(_)
650 | ScalarExpr::InternalColumn(_)
651 | ScalarExpr::QualifiedColumn { .. }
652 | ScalarExpr::Literal(_)
653 | ScalarExpr::TypedLiteral { .. }
654 | ScalarExpr::Param(_) => {}
655 }
656 Ok(())
657}
658
659fn collect_many_routine_references(
660 expressions: &[ScalarExpr],
661 subqueries: &[QueryPlan],
662 references: &mut Vec<BoundRoutineReference>,
663) -> Result<(), SQLError> {
664 for expression in expressions {
665 collect_scalar_routine_references(expression, subqueries, references)?;
666 }
667 Ok(())
668}
669
670fn collect_case_routine_references(
671 base: Option<&ScalarExpr>,
672 when: &[(ScalarExpr, ScalarExpr)],
673 else_branch: Option<&ScalarExpr>,
674 subqueries: &[QueryPlan],
675 references: &mut Vec<BoundRoutineReference>,
676) -> Result<(), SQLError> {
677 if let Some(base) = base {
678 collect_scalar_routine_references(base, subqueries, references)?;
679 }
680 for (condition, result) in when {
681 collect_scalar_routine_references(condition, subqueries, references)?;
682 collect_scalar_routine_references(result, subqueries, references)?;
683 }
684 if let Some(branch) = else_branch {
685 collect_scalar_routine_references(branch, subqueries, references)?;
686 }
687 Ok(())
688}
689
690fn collect_window_routine_references(
691 name: &str,
692 args: &[ScalarExpr],
693 spec: &crate::ScalarWindowSpec,
694 subqueries: &[QueryPlan],
695 references: &mut Vec<BoundRoutineReference>,
696) -> Result<(), SQLError> {
697 for argument in args {
698 collect_scalar_routine_references(argument, subqueries, references)?;
699 }
700 for expression in &spec.partition_by {
701 collect_scalar_routine_references(expression, subqueries, references)?;
702 }
703 for order in &spec.order_by {
704 collect_scalar_routine_references(&order.expr, subqueries, references)?;
705 }
706 if let Some(frame) = &spec.frame {
707 for bound in [&frame.start, &frame.end] {
708 if let ScalarFrameBound::Preceding(inner) | ScalarFrameBound::Following(inner) = bound {
709 collect_scalar_routine_references(inner, subqueries, references)?;
710 }
711 }
712 }
713 references.push(BoundRoutineReference {
714 name: name.to_string(),
715 binding: None,
716 });
717 Ok(())
718}
719
720fn mark_cte_relations_bound(body: &mut crate::plan::CtePlanBody) {
721 match body {
722 crate::plan::CtePlanBody::Query(query) => mark_query_relations_bound(query),
723 crate::plan::CtePlanBody::Command(command) => {
724 match command.as_mut() {
725 CommandPlan::Insert(plan) => {
726 plan.relations_bound = true;
727 plan.target_relation_bound = true;
728 }
729 CommandPlan::Update(plan) => {
730 plan.relations_bound = true;
731 plan.target_relation_bound = true;
732 }
733 CommandPlan::Delete(plan) => {
734 plan.relations_bound = true;
735 plan.target_relation_bound = true;
736 }
737 _ => {}
738 }
739 if let Some(ctes) = command.ctes_mut() {
740 for cte in ctes {
741 mark_cte_relations_bound(&mut cte.body);
742 }
743 }
744 for query in command.query_inputs_mut() {
745 mark_query_relations_bound(query);
746 }
747 if let Some(source) = command.source_input_mut() {
748 mark_source_relations_bound(source);
749 }
750 }
751 }
752}
753
754fn collect_cte_routine_references(
755 body: &crate::plan::CtePlanBody,
756 references: &mut Vec<BoundRoutineReference>,
757) -> Result<(), SQLError> {
758 match body {
759 crate::plan::CtePlanBody::Query(query) => {
760 collect_query_routine_references(query, references)
761 }
762 crate::plan::CtePlanBody::Command(command) => {
763 for cte in command.ctes() {
764 collect_cte_routine_references(&cte.body, references)?;
765 if let Some(cycle) = &cte.cycle {
766 collect_scalar_routine_references(&cycle.mark_value, &[], references)?;
767 collect_scalar_routine_references(&cycle.mark_default, &[], references)?;
768 }
769 }
770 for query in command.query_inputs() {
771 collect_query_routine_references(query, references)?;
772 }
773 if let Some(source) = command.source_input() {
774 collect_source_routine_references(source, command.scalar_subqueries(), references)?;
775 }
776 for expression in command.expressions() {
777 collect_scalar_routine_references(
778 expression,
779 command.scalar_subqueries(),
780 references,
781 )?;
782 }
783 Ok(())
784 }
785 }
786}
787
788fn mark_query_relations_bound(query: &mut QueryPlan) {
789 query.relations_bound = true;
790 for cte in &mut query.ctes {
791 mark_cte_relations_bound(&mut cte.body);
792 }
793 match &mut query.root {
794 RelationalPlan::QueryBlock(block) => {
795 if let Some(source) = &mut block.from {
796 mark_source_relations_bound(source);
797 }
798 for subquery in &mut block.subqueries {
799 mark_query_relations_bound(subquery);
800 }
801 }
802 RelationalPlan::SetOp {
803 left,
804 right,
805 subqueries,
806 ..
807 } => {
808 mark_query_relations_bound(left);
809 mark_query_relations_bound(right);
810 for subquery in subqueries {
811 mark_query_relations_bound(subquery);
812 }
813 }
814 RelationalPlan::Values { subqueries, .. } => {
815 for subquery in subqueries {
816 mark_query_relations_bound(subquery);
817 }
818 }
819 }
820}
821
822fn mark_source_relations_bound(source: &mut SourcePlan) {
823 match source {
824 SourcePlan::Join { left, right, .. } => {
825 mark_source_relations_bound(left);
826 mark_source_relations_bound(right);
827 }
828 SourcePlan::Subquery { body, .. } => mark_query_relations_bound(body),
829 SourcePlan::Table { .. }
830 | SourcePlan::Values { .. }
831 | SourcePlan::Function { .. }
832 | SourcePlan::FunctionGroup { .. } => {}
833 }
834}
835
836pub mod analysis;