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