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 expressions.extend(condition.iter().cloned());
246 expressions.extend(
247 columns
248 .iter()
249 .flat_map(crate::ast::AssignmentTarget::expressions)
250 .cloned(),
251 );
252 expressions.extend(values.iter().cloned());
253 }
254 crate::plan::MergeWhenPlan::DeleteMatched { condition }
255 | crate::plan::MergeWhenPlan::DeleteNotMatchedBySource { condition }
256 | crate::plan::MergeWhenPlan::NothingMatched { condition }
257 | crate::plan::MergeWhenPlan::NothingNotMatched { condition }
258 | crate::plan::MergeWhenPlan::NothingNotMatchedBySource { condition } => {
259 expressions.extend(condition.iter().cloned());
260 }
261 }
262 }
263 expressions.extend(
264 plan.returning
265 .iter()
266 .map(|projection| projection.expr.clone()),
267 );
268 CommandRoutineInputs {
269 ctes: plan.ctes.clone(),
270 source: Some(source),
271 expressions,
272 subqueries: plan.subqueries.clone(),
273 outer: RowSchema::default(),
274 }
275}
276
277fn insert_statement_routine_inputs(
278 context: &CatalogRoutineContext<'_, '_>,
279 plan: &InsertPlan,
280) -> Result<CommandRoutineInputs, SQLError> {
281 let mut expressions = plan
282 .columns
283 .iter()
284 .flat_map(crate::ast::AssignmentTarget::expressions)
285 .chain(plan.rows.iter().flatten())
286 .cloned()
287 .collect::<Vec<_>>();
288 if let Some(conflict) = &plan.on_conflict {
289 expressions.extend(conflict.expressions.iter().cloned());
290 expressions.extend(conflict.predicate.iter().map(Box::as_ref).cloned());
291 if let ConflictActionPlan::Update {
292 assignments,
293 predicate,
294 } = &conflict.action
295 {
296 expressions.extend(
297 assignments
298 .iter()
299 .flat_map(crate::plan::AssignmentPlan::expressions)
300 .cloned(),
301 );
302 expressions.extend(predicate.iter().map(Box::as_ref).cloned());
303 }
304 }
305 expressions.extend(
306 plan.returning
307 .iter()
308 .map(|projection| projection.expr.clone()),
309 );
310 let source = plan.source.as_ref().map(|source| SourcePlan::Subquery {
311 body: Box::new((**source).clone()),
312 alias: Some("__uqa_catalog_statement_source".into()),
313 column_aliases: Vec::new(),
314 });
315 Ok(CommandRoutineInputs {
316 ctes: plan.ctes.clone(),
317 source,
318 expressions,
319 subqueries: plan.subqueries.clone(),
320 outer: statement_target_outer_schema(
321 context,
322 &plan.table,
323 &plan.target_qualifier,
324 &plan.returning_aliases,
325 )?,
326 })
327}
328
329fn update_statement_routine_inputs(
330 context: &CatalogRoutineContext<'_, '_>,
331 plan: &UpdatePlan,
332) -> Result<CommandRoutineInputs, SQLError> {
333 let mut expressions = plan
334 .assignments
335 .iter()
336 .flat_map(crate::plan::AssignmentPlan::expressions)
337 .cloned()
338 .collect::<Vec<_>>();
339 expressions.extend(plan.predicate.iter().cloned());
340 expressions.extend(
341 plan.returning
342 .iter()
343 .map(|projection| projection.expr.clone()),
344 );
345 Ok(CommandRoutineInputs {
346 ctes: plan.ctes.clone(),
347 source: plan.source.as_deref().cloned(),
348 expressions,
349 subqueries: plan.subqueries.clone(),
350 outer: statement_target_outer_schema(
351 context,
352 &plan.table,
353 &plan.target_qualifier,
354 &plan.returning_aliases,
355 )?,
356 })
357}
358
359fn delete_statement_routine_inputs(
360 context: &CatalogRoutineContext<'_, '_>,
361 plan: &DeletePlan,
362) -> Result<CommandRoutineInputs, SQLError> {
363 let mut expressions = plan.predicate.iter().cloned().collect::<Vec<_>>();
364 expressions.extend(
365 plan.returning
366 .iter()
367 .map(|projection| projection.expr.clone()),
368 );
369 Ok(CommandRoutineInputs {
370 ctes: plan.ctes.clone(),
371 source: plan.source.as_deref().cloned(),
372 expressions,
373 subqueries: plan.subqueries.clone(),
374 outer: statement_target_outer_schema(
375 context,
376 &plan.table,
377 &plan.target_qualifier,
378 &plan.returning_aliases,
379 )?,
380 })
381}
382
383fn expression_contains_routine(expression: &ScalarExpr, subqueries: &[QueryPlan]) -> bool {
384 let mut references = Vec::new();
385 collect_scalar_routine_references(expression, subqueries, &mut references).is_ok()
386 && !references.is_empty()
387}
388
389fn statement_target_outer_schema(
390 context: &CatalogRoutineContext<'_, '_>,
391 table: &str,
392 target_qualifier: &str,
393 aliases: &crate::ast::ReturningAliases,
394) -> Result<RowSchema, SQLError> {
395 let target = crate::binding::analyze_source_plan_schema(
396 context.routines,
397 &SourcePlan::Table {
398 bound_columns: None,
399 name: table.to_string(),
400 qualifier: target_qualifier.to_string(),
401 alias: None,
402 column_aliases: Vec::new(),
403 include_descendants: true,
404 },
405 &[],
406 context.binding,
407 None,
408 )?;
409 let target = RowSchema::with_types(target.columns().to_vec(), target.column_types().to_vec());
410 Ok(crate::semantics::returning_expression_schema(
411 &target,
412 target_qualifier,
413 aliases,
414 None,
415 ))
416}
417
418pub fn collect_expression_routine_references(
419 expression: &crate::plan::ExpressionPlan,
420) -> Result<Vec<BoundRoutineReference>, SQLError> {
421 let mut references = Vec::new();
422 collect_scalar_routine_references(&expression.scalar, &expression.subqueries, &mut references)?;
423 Ok(references)
424}
425
426fn collect_query_routine_references(
427 query: &QueryPlan,
428 references: &mut Vec<BoundRoutineReference>,
429) -> Result<(), SQLError> {
430 for cte in &query.ctes {
431 collect_cte_routine_references(&cte.body, references)?;
432 if let Some(cycle) = &cte.cycle {
433 collect_scalar_routine_references(&cycle.mark_value, &[], references)?;
434 collect_scalar_routine_references(&cycle.mark_default, &[], references)?;
435 }
436 }
437 match &query.root {
438 RelationalPlan::QueryBlock(block) => {
439 if let Some(source) = &block.from {
440 collect_source_routine_references(source, &block.subqueries, references)?;
441 }
442 for projection in &block.projections {
443 collect_scalar_routine_references(&projection.expr, &block.subqueries, references)?;
444 }
445 if let Some(expression) = &block.r#where {
446 collect_scalar_routine_references(expression, &block.subqueries, references)?;
447 }
448 for expression in &block.group_by {
449 collect_scalar_routine_references(expression, &block.subqueries, references)?;
450 }
451 for expression in block.grouping_sets.iter().flatten() {
452 collect_scalar_routine_references(expression, &block.subqueries, references)?;
453 }
454 if let Some(expression) = &block.having {
455 collect_scalar_routine_references(expression, &block.subqueries, references)?;
456 }
457 for order in &block.order_by {
458 collect_scalar_routine_references(&order.expr, &block.subqueries, references)?;
459 }
460 if let Some(expression) = &block.limit {
461 collect_scalar_routine_references(expression, &block.subqueries, references)?;
462 }
463 if let Some(expression) = &block.offset {
464 collect_scalar_routine_references(expression, &block.subqueries, references)?;
465 }
466 for expression in &block.distinct_on {
467 collect_scalar_routine_references(expression, &block.subqueries, references)?;
468 }
469 }
470 RelationalPlan::SetOp {
471 left,
472 right,
473 order_by,
474 limit,
475 offset,
476 subqueries,
477 ..
478 } => {
479 collect_query_routine_references(left, references)?;
480 collect_query_routine_references(right, references)?;
481 for order in order_by {
482 collect_scalar_routine_references(&order.expr, subqueries, references)?;
483 }
484 if let Some(expression) = limit {
485 collect_scalar_routine_references(expression, subqueries, references)?;
486 }
487 if let Some(expression) = offset {
488 collect_scalar_routine_references(expression, subqueries, references)?;
489 }
490 }
491 RelationalPlan::Values { rows, subqueries } => {
492 for expression in rows.iter().flatten() {
493 collect_scalar_routine_references(expression, subqueries, references)?;
494 }
495 }
496 }
497 Ok(())
498}
499
500fn collect_source_routine_references(
501 source: &SourcePlan,
502 subqueries: &[QueryPlan],
503 references: &mut Vec<BoundRoutineReference>,
504) -> Result<(), SQLError> {
505 match source {
506 SourcePlan::Table { .. } => {}
507 SourcePlan::Join {
508 left, right, on, ..
509 } => {
510 collect_source_routine_references(left, subqueries, references)?;
511 collect_source_routine_references(right, subqueries, references)?;
512 if let Some(expression) = on {
513 collect_scalar_routine_references(expression, subqueries, references)?;
514 }
515 }
516 SourcePlan::Values { rows, .. } => {
517 for expression in rows.iter().flatten() {
518 collect_scalar_routine_references(expression, subqueries, references)?;
519 }
520 }
521 SourcePlan::Function {
522 name,
523 binding,
524 args,
525 ..
526 } => {
527 references.push(BoundRoutineReference {
528 name: name.clone(),
529 binding: binding.clone(),
530 });
531 for expression in args {
532 collect_scalar_routine_references(expression, subqueries, references)?;
533 }
534 }
535 SourcePlan::FunctionGroup { functions, .. } => {
536 for function in functions {
537 references.push(BoundRoutineReference {
538 name: function.name.clone(),
539 binding: function.binding.clone(),
540 });
541 for expression in &function.args {
542 collect_scalar_routine_references(expression, subqueries, references)?;
543 }
544 }
545 }
546 SourcePlan::Subquery { body, .. } => {
547 collect_query_routine_references(body, references)?;
548 }
549 }
550 Ok(())
551}
552
553fn collect_scalar_routine_references(
554 expression: &ScalarExpr,
555 subqueries: &[QueryPlan],
556 references: &mut Vec<BoundRoutineReference>,
557) -> Result<(), SQLError> {
558 match expression {
559 ScalarExpr::Func {
560 name,
561 binding,
562 args,
563 order_by,
564 filter,
565 ..
566 } => {
567 for argument in args {
568 collect_scalar_routine_references(argument, subqueries, references)?;
569 }
570 for order in order_by {
571 collect_scalar_routine_references(&order.expr, subqueries, references)?;
572 }
573 if let Some(filter) = filter {
574 collect_scalar_routine_references(filter, subqueries, references)?;
575 }
576 references.push(BoundRoutineReference {
577 name: name.clone(),
578 binding: binding.clone(),
579 });
580 }
581 ScalarExpr::Array(items)
582 | ScalarExpr::Row(items)
583 | ScalarExpr::And(items)
584 | ScalarExpr::Or(items) => collect_many_routine_references(items, subqueries, references)?,
585 ScalarExpr::Binary { lhs, rhs, .. } => {
586 collect_scalar_routine_references(lhs, subqueries, references)?;
587 collect_scalar_routine_references(rhs, subqueries, references)?;
588 }
589 ScalarExpr::UnaryMinus(inner)
590 | ScalarExpr::Not(inner)
591 | ScalarExpr::IsNull { expr: inner, .. }
592 | ScalarExpr::Cast { expr: inner, .. } => {
593 collect_scalar_routine_references(inner, subqueries, references)?;
594 }
595 ScalarExpr::Between { expr, low, high } => {
596 collect_scalar_routine_references(expr, subqueries, references)?;
597 collect_scalar_routine_references(low, subqueries, references)?;
598 collect_scalar_routine_references(high, subqueries, references)?;
599 }
600 ScalarExpr::InList { expr, list, .. } => {
601 collect_scalar_routine_references(expr, subqueries, references)?;
602 for item in list {
603 collect_scalar_routine_references(item, subqueries, references)?;
604 }
605 }
606 ScalarExpr::WindowCall { name, args, spec } => {
607 collect_window_routine_references(name, args, spec, subqueries, references)?;
608 }
609 ScalarExpr::Case {
610 base,
611 when,
612 else_branch,
613 } => collect_case_routine_references(
614 base.as_deref(),
615 when,
616 else_branch.as_deref(),
617 subqueries,
618 references,
619 )?,
620 ScalarExpr::ScalarSubquery(index)
621 | ScalarExpr::Exists {
622 subquery: index, ..
623 } => {
624 let query = subqueries.get(*index).ok_or_else(|| {
625 SQLError::Internal(format!(
626 "stored catalog routine binding cannot resolve subquery slot {index}"
627 ))
628 })?;
629 collect_query_routine_references(query, references)?;
630 }
631 ScalarExpr::InSubquery {
632 expr,
633 subquery: index,
634 ..
635 } => {
636 collect_scalar_routine_references(expr, subqueries, references)?;
637 let query = subqueries.get(*index).ok_or_else(|| {
638 SQLError::Internal(format!(
639 "stored catalog routine binding cannot resolve subquery slot {index}"
640 ))
641 })?;
642 collect_query_routine_references(query, references)?;
643 }
644 ScalarExpr::Star
645 | ScalarExpr::QualifiedStar(_)
646 | ScalarExpr::Default
647 | ScalarExpr::Column(_)
648 | ScalarExpr::Position(_)
649 | ScalarExpr::InternalColumn(_)
650 | ScalarExpr::QualifiedColumn { .. }
651 | ScalarExpr::Literal(_)
652 | ScalarExpr::TypedLiteral { .. }
653 | ScalarExpr::Param(_) => {}
654 }
655 Ok(())
656}
657
658fn collect_many_routine_references(
659 expressions: &[ScalarExpr],
660 subqueries: &[QueryPlan],
661 references: &mut Vec<BoundRoutineReference>,
662) -> Result<(), SQLError> {
663 for expression in expressions {
664 collect_scalar_routine_references(expression, subqueries, references)?;
665 }
666 Ok(())
667}
668
669fn collect_case_routine_references(
670 base: Option<&ScalarExpr>,
671 when: &[(ScalarExpr, ScalarExpr)],
672 else_branch: Option<&ScalarExpr>,
673 subqueries: &[QueryPlan],
674 references: &mut Vec<BoundRoutineReference>,
675) -> Result<(), SQLError> {
676 if let Some(base) = base {
677 collect_scalar_routine_references(base, subqueries, references)?;
678 }
679 for (condition, result) in when {
680 collect_scalar_routine_references(condition, subqueries, references)?;
681 collect_scalar_routine_references(result, subqueries, references)?;
682 }
683 if let Some(branch) = else_branch {
684 collect_scalar_routine_references(branch, subqueries, references)?;
685 }
686 Ok(())
687}
688
689fn collect_window_routine_references(
690 name: &str,
691 args: &[ScalarExpr],
692 spec: &crate::ScalarWindowSpec,
693 subqueries: &[QueryPlan],
694 references: &mut Vec<BoundRoutineReference>,
695) -> Result<(), SQLError> {
696 for argument in args {
697 collect_scalar_routine_references(argument, subqueries, references)?;
698 }
699 for expression in &spec.partition_by {
700 collect_scalar_routine_references(expression, subqueries, references)?;
701 }
702 for order in &spec.order_by {
703 collect_scalar_routine_references(&order.expr, subqueries, references)?;
704 }
705 if let Some(frame) = &spec.frame {
706 for bound in [&frame.start, &frame.end] {
707 if let ScalarFrameBound::Preceding(inner) | ScalarFrameBound::Following(inner) = bound {
708 collect_scalar_routine_references(inner, subqueries, references)?;
709 }
710 }
711 }
712 references.push(BoundRoutineReference {
713 name: name.to_string(),
714 binding: None,
715 });
716 Ok(())
717}
718
719fn mark_cte_relations_bound(body: &mut crate::plan::CtePlanBody) {
720 match body {
721 crate::plan::CtePlanBody::Query(query) => mark_query_relations_bound(query),
722 crate::plan::CtePlanBody::Command(command) => {
723 match command.as_mut() {
724 CommandPlan::Insert(plan) => {
725 plan.relations_bound = true;
726 plan.target_relation_bound = true;
727 }
728 CommandPlan::Update(plan) => {
729 plan.relations_bound = true;
730 plan.target_relation_bound = true;
731 }
732 CommandPlan::Delete(plan) => {
733 plan.relations_bound = true;
734 plan.target_relation_bound = true;
735 }
736 _ => {}
737 }
738 if let Some(ctes) = command.ctes_mut() {
739 for cte in ctes {
740 mark_cte_relations_bound(&mut cte.body);
741 }
742 }
743 for query in command.query_inputs_mut() {
744 mark_query_relations_bound(query);
745 }
746 if let Some(source) = command.source_input_mut() {
747 mark_source_relations_bound(source);
748 }
749 }
750 }
751}
752
753fn collect_cte_routine_references(
754 body: &crate::plan::CtePlanBody,
755 references: &mut Vec<BoundRoutineReference>,
756) -> Result<(), SQLError> {
757 match body {
758 crate::plan::CtePlanBody::Query(query) => {
759 collect_query_routine_references(query, references)
760 }
761 crate::plan::CtePlanBody::Command(command) => {
762 for cte in command.ctes() {
763 collect_cte_routine_references(&cte.body, references)?;
764 if let Some(cycle) = &cte.cycle {
765 collect_scalar_routine_references(&cycle.mark_value, &[], references)?;
766 collect_scalar_routine_references(&cycle.mark_default, &[], references)?;
767 }
768 }
769 for query in command.query_inputs() {
770 collect_query_routine_references(query, references)?;
771 }
772 if let Some(source) = command.source_input() {
773 collect_source_routine_references(source, command.scalar_subqueries(), references)?;
774 }
775 for expression in command.expressions() {
776 collect_scalar_routine_references(
777 expression,
778 command.scalar_subqueries(),
779 references,
780 )?;
781 }
782 Ok(())
783 }
784 }
785}
786
787fn mark_query_relations_bound(query: &mut QueryPlan) {
788 query.relations_bound = true;
789 for cte in &mut query.ctes {
790 mark_cte_relations_bound(&mut cte.body);
791 }
792 match &mut query.root {
793 RelationalPlan::QueryBlock(block) => {
794 if let Some(source) = &mut block.from {
795 mark_source_relations_bound(source);
796 }
797 for subquery in &mut block.subqueries {
798 mark_query_relations_bound(subquery);
799 }
800 }
801 RelationalPlan::SetOp {
802 left,
803 right,
804 subqueries,
805 ..
806 } => {
807 mark_query_relations_bound(left);
808 mark_query_relations_bound(right);
809 for subquery in subqueries {
810 mark_query_relations_bound(subquery);
811 }
812 }
813 RelationalPlan::Values { subqueries, .. } => {
814 for subquery in subqueries {
815 mark_query_relations_bound(subquery);
816 }
817 }
818 }
819}
820
821fn mark_source_relations_bound(source: &mut SourcePlan) {
822 match source {
823 SourcePlan::Join { left, right, .. } => {
824 mark_source_relations_bound(left);
825 mark_source_relations_bound(right);
826 }
827 SourcePlan::Subquery { body, .. } => mark_query_relations_bound(body),
828 SourcePlan::Table { .. }
829 | SourcePlan::Values { .. }
830 | SourcePlan::Function { .. }
831 | SourcePlan::FunctionGroup { .. } => {}
832 }
833}
834
835pub mod analysis;