1use super::{
10 cte_references_own_name, extend_cte_generated_schema, extend_recursive_cte_binding_schema,
11 operator_join_relation_schemas, overlay_outer_schema, rename_schema, BindingContext,
12 ColumnType, QueryPlan, RelationalPlan, RowSchema, SQLError, SQLParam, ScalarExpr, SchemaScope,
13 SourcePlan,
14};
15use crate::ast::FunctionBinding;
16use crate::plan::ExpressionPlan;
17use crate::routines::RoutineResolution;
18use crate::{ColumnIdentity, FunctionTypeResolver};
19
20impl SchemaScope {
21 pub(super) fn with_stored_outer_internal_aliases(&self, schema: &RowSchema) -> RowSchema {
22 self.stored_expression_outer.as_ref().map_or_else(
23 || schema.clone(),
24 |outer| {
25 if schema.physical_width() < outer.physical_width() {
26 schema.clone()
27 } else {
28 RowSchema::with_trailing_internal_aliases(schema, outer)
29 }
30 },
31 )
32 }
33
34 fn canonicalize_stored_outer_columns(&self, expression: &mut ScalarExpr, schema: &RowSchema) {
35 let Some(outer) = self.stored_expression_outer.as_ref() else {
36 return;
37 };
38 let Some(outer_start) = schema.physical_width().checked_sub(outer.physical_width()) else {
39 return;
40 };
41 crate::plan::rewrite_scalar_expression(expression, &mut |node| {
42 let lookup = match node {
43 ScalarExpr::Column(column) => ColumnIdentity::unqualified(column.as_str()),
44 ScalarExpr::QualifiedColumn { qualifier, column } => {
45 ColumnIdentity::qualified(qualifier.as_str(), column.as_str())
46 }
47 _ => return,
48 };
49 let Some(slot) = schema.physical_slot_for_identity(&lookup) else {
50 return;
51 };
52 if slot < outer_start {
53 return;
54 }
55 let public_qualifier = match node {
56 ScalarExpr::QualifiedColumn { qualifier, .. } => Some(qualifier.as_str()),
57 ScalarExpr::Column(column) => {
58 let mut qualifiers = outer
59 .identities()
60 .iter()
61 .filter(|identity| identity.column() == column)
62 .filter_map(ColumnIdentity::qualifier);
63 let qualifier = qualifiers.next();
64 (qualifiers.next().is_none()).then_some(qualifier).flatten()
65 }
66 _ => None,
67 };
68 let Some(public_qualifier) = public_qualifier.filter(|qualifier| {
69 qualifier.eq_ignore_ascii_case("old") || qualifier.eq_ignore_ascii_case("new")
70 }) else {
71 return;
72 };
73 let outer_lookup = ColumnIdentity::qualified(public_qualifier, lookup.column());
74 let Some(outer_slot) = outer.physical_slot_for_identity(&outer_lookup) else {
75 return;
76 };
77 let Some(column) = outer.unique_internal_column_for_slot(outer_slot) else {
78 return;
79 };
80 *node = ScalarExpr::InternalColumn(column);
81 });
82 }
83
84 fn bind_cte_routines_for_storage(
85 &mut self,
86 routines: &dyn RoutineResolution,
87 body: &mut crate::plan::CtePlanBody,
88 params: &[SQLParam],
89 outer: Option<&RowSchema>,
90 ) -> Result<RowSchema, SQLError> {
91 let crate::plan::CtePlanBody::Command(command) = body else {
92 return self.bind_query_routines_for_storage(
93 routines,
94 body.query_mut().expect("query CTE body"),
95 params,
96 outer,
97 );
98 };
99 let previous = match command.ctes_mut() {
100 Some(ctes) => self.bind_cte_routine_schemas(routines, ctes, params, None)?,
101 None => Vec::new(),
102 };
103 let result = (|| {
104 let (_, expression) = self.command_expression_schema(routines, command, params)?;
105 let subqueries = command.scalar_subqueries().to_vec();
106 if let Some(source) = command.source_input_mut() {
107 self.bind_source_routines_for_storage(routines, source, &subqueries, params, None)?;
108 }
109 for query in command.query_inputs_mut() {
110 self.bind_query_routines_for_storage(routines, query, params, Some(&expression))?;
111 }
112 let subqueries = command.scalar_subqueries().to_vec();
113 for scalar in command.expressions_mut() {
114 self.bind_scalar_routines_for_storage(
115 routines,
116 scalar,
117 &expression,
118 &subqueries,
119 params,
120 None,
121 )?;
122 }
123 self.bind_command_returning(routines, command, params)
124 })();
125 self.restore_cte_schemas(previous);
126 result
127 }
128
129 fn bind_cte_routine_schemas(
130 &mut self,
131 routines: &dyn RoutineResolution,
132 ctes: &mut [crate::plan::CtePlan],
133 params: &[SQLParam],
134 outer: Option<&RowSchema>,
135 ) -> Result<Vec<(String, bool, Option<RowSchema>)>, SQLError> {
136 let ordered_names = crate::semantics::ordered_cte_plans(ctes)?
137 .into_iter()
138 .map(|cte| cte.name.clone())
139 .collect::<Vec<_>>();
140 let mut previous = Vec::with_capacity(ordered_names.len());
141 for name in ordered_names {
142 let position = ctes
143 .iter()
144 .position(|cte| cte.name == name)
145 .ok_or_else(|| SQLError::Internal(format!("ordered CTE `{name}` disappeared")))?;
146 let self_recursive = cte_references_own_name(&ctes[position]);
147 if let Some(cycle) = ctes[position].cycle.as_mut() {
148 let schema = RowSchema::default();
149 self.bind_scalar_routines_for_storage(
150 routines,
151 &mut cycle.mark_value,
152 &schema,
153 &[],
154 params,
155 outer,
156 )?;
157 self.bind_scalar_routines_for_storage(
158 routines,
159 &mut cycle.mark_default,
160 &schema,
161 &[],
162 params,
163 outer,
164 )?;
165 }
166 let provisional = if self_recursive {
167 self.bind_recursive_seed(
168 routines,
169 ctes[position]
170 .body
171 .query()
172 .ok_or_else(|| SQLError::Routine {
173 sqlstate: "42P19".into(),
174 message: format!(
175 "recursive query \"{}\" must not contain data-modifying statements",
176 ctes[position].name
177 ),
178 })?,
179 params,
180 outer,
181 )?
182 } else {
183 self.bind_cte_routines_for_storage(
184 routines,
185 &mut ctes[position].body,
186 params,
187 outer,
188 )?
189 };
190 let columns = ctes[position].columns.clone();
191 let provisional = rename_schema(&provisional, &columns, None);
192 let provisional = if self_recursive {
193 extend_recursive_cte_binding_schema(routines, &ctes[position], provisional, params)?
194 } else {
195 extend_cte_generated_schema(routines, &ctes[position], provisional, params)?
196 };
197 previous.push((
198 name.clone(),
199 self.set_cte_returning(&ctes[position]),
200 self.ctes.insert(name.clone(), provisional),
201 ));
202 if self_recursive {
203 let complete = self.bind_cte_routines_for_storage(
204 routines,
205 &mut ctes[position].body,
206 params,
207 outer,
208 )?;
209 let complete = rename_schema(&complete, &columns, None);
210 let complete =
211 extend_cte_generated_schema(routines, &ctes[position], complete, params)?;
212 self.ctes.insert(name, complete);
213 }
214 }
215
216 Ok(previous)
217 }
218
219 fn bind_query_routines_for_storage(
220 &mut self,
221 routines: &dyn RoutineResolution,
222 plan: &mut QueryPlan,
223 params: &[SQLParam],
224 outer: Option<&RowSchema>,
225 ) -> Result<RowSchema, SQLError> {
226 let previous = self.bind_cte_routine_schemas(routines, &mut plan.ctes, params, outer)?;
227
228 let result = self.bind_root_routines_for_storage(routines, &mut plan.root, params, outer);
229 self.restore_cte_schemas(previous);
230 result
231 }
232
233 #[expect(
234 clippy::too_many_lines,
235 reason = "preserves SELECT schema and row identity"
236 )]
237 fn bind_root_routines_for_storage(
238 &mut self,
239 routines: &dyn RoutineResolution,
240 root: &mut RelationalPlan,
241 params: &[SQLParam],
242 outer: Option<&RowSchema>,
243 ) -> Result<RowSchema, SQLError> {
244 match root {
245 RelationalPlan::QueryBlock(block) => {
246 let source_schema = match block.from.as_mut() {
247 Some(source) => self.bind_source_for_execution(
248 routines,
249 source,
250 &block.subqueries,
251 params,
252 outer,
253 )?,
254 None => RowSchema::default(),
255 };
256 if let Some(source) = block.from.as_mut() {
257 self.bind_source_routines_for_storage(
258 routines,
259 source,
260 &block.subqueries,
261 params,
262 outer,
263 )?;
264 }
265 let expression_schema = overlay_outer_schema(&source_schema, outer);
266 for subquery in &mut block.subqueries {
267 self.bind_query_routines_for_storage(
268 routines,
269 subquery,
270 params,
271 Some(&expression_schema),
272 )?;
273 }
274 }
275 RelationalPlan::SetOp {
276 left,
277 right,
278 subqueries,
279 ..
280 } => {
281 self.bind_query_routines_for_storage(routines, left, params, outer)?;
282 self.bind_query_routines_for_storage(routines, right, params, outer)?;
283 for subquery in subqueries {
284 self.bind_query_routines_for_storage(routines, subquery, params, outer)?;
285 }
286 }
287 RelationalPlan::Values { subqueries, .. } => {
288 for subquery in subqueries {
289 self.bind_query_routines_for_storage(routines, subquery, params, outer)?;
290 }
291 }
292 }
293
294 let set_output = match &*root {
295 RelationalPlan::SetOp { .. } => {
296 Some(self.bind_root(routines, root, params, outer, false)?)
297 }
298 RelationalPlan::QueryBlock(_) | RelationalPlan::Values { .. } => None,
299 };
300 match root {
301 RelationalPlan::QueryBlock(block) => {
302 if block.from.is_none()
303 && block
304 .projections
305 .iter()
306 .any(|projection| matches!(projection.expr, ScalarExpr::Star))
307 && outer.is_none()
308 {
309 return Err(SQLError::Routine {
310 sqlstate: "42601".into(),
311 message: "SELECT * with no tables specified is not valid".into(),
312 });
313 }
314 let source_schema = block.from.as_ref().map_or_else(
315 || Ok(RowSchema::default()),
316 |source| self.bind_source(routines, source, &block.subqueries, params, outer),
317 )?;
318 let expression_schema = overlay_outer_schema(&source_schema, outer);
319 if let Some(filter) = block.r#where.as_mut() {
320 self.bind_scalar_routines_for_storage(
321 routines,
322 filter,
323 &expression_schema,
324 &block.subqueries,
325 params,
326 outer,
327 )?;
328 }
329 for projection in &mut block.projections {
330 self.bind_scalar_routines_for_storage(
331 routines,
332 &mut projection.expr,
333 &expression_schema,
334 &block.subqueries,
335 params,
336 outer,
337 )?;
338 }
339 block.projections = crate::semantics::expand_bound_projection_stars(
340 &block.projections,
341 &source_schema,
342 )?;
343 crate::semantics::grouping_sets::bind_grouping_names(
344 routines,
345 block,
346 &source_schema,
347 params,
348 )?;
349 for expression in &mut block.group_by {
350 self.bind_scalar_routines_for_storage(
351 routines,
352 expression,
353 &expression_schema,
354 &block.subqueries,
355 params,
356 outer,
357 )?;
358 }
359 for set in &mut block.grouping_sets {
360 for expression in set {
361 self.bind_scalar_routines_for_storage(
362 routines,
363 expression,
364 &expression_schema,
365 &block.subqueries,
366 params,
367 outer,
368 )?;
369 }
370 }
371 if let Some(having) = block.having.as_mut() {
372 self.bind_scalar_routines_for_storage(
373 routines,
374 having,
375 &expression_schema,
376 &block.subqueries,
377 params,
378 outer,
379 )?;
380 }
381 for order in &mut block.order_by {
382 self.bind_scalar_routines_for_storage(
383 routines,
384 &mut order.expr,
385 &expression_schema,
386 &block.subqueries,
387 params,
388 outer,
389 )?;
390 }
391 if let Some(limit) = block.limit.as_mut() {
392 self.bind_scalar_routines_for_storage(
393 routines,
394 limit,
395 &expression_schema,
396 &block.subqueries,
397 params,
398 outer,
399 )?;
400 }
401 if let Some(offset) = block.offset.as_mut() {
402 self.bind_scalar_routines_for_storage(
403 routines,
404 offset,
405 &expression_schema,
406 &block.subqueries,
407 params,
408 outer,
409 )?;
410 }
411 for expression in &mut block.distinct_on {
412 self.bind_scalar_routines_for_storage(
413 routines,
414 expression,
415 &expression_schema,
416 &block.subqueries,
417 params,
418 outer,
419 )?;
420 }
421 }
422 RelationalPlan::SetOp {
423 order_by,
424 limit,
425 offset,
426 subqueries,
427 ..
428 } => {
429 let output = set_output
430 .as_ref()
431 .expect("set-operation output schema was bound before routine expressions");
432 for order in order_by {
433 self.bind_scalar_routines_for_storage(
434 routines,
435 &mut order.expr,
436 output,
437 subqueries,
438 params,
439 outer,
440 )?;
441 }
442 if let Some(limit) = limit {
443 self.bind_scalar_routines_for_storage(
444 routines, limit, output, subqueries, params, outer,
445 )?;
446 }
447 if let Some(offset) = offset {
448 self.bind_scalar_routines_for_storage(
449 routines, offset, output, subqueries, params, outer,
450 )?;
451 }
452 }
453 RelationalPlan::Values { rows, subqueries } => {
454 let input = outer.cloned().unwrap_or_default();
455 for expression in rows.iter_mut().flatten() {
456 self.bind_scalar_routines_for_storage(
457 routines, expression, &input, subqueries, params, outer,
458 )?;
459 }
460 }
461 }
462 self.bind_root(routines, root, params, outer, false)
463 }
464
465 #[expect(
466 clippy::too_many_lines,
467 reason = "preserves SELECT schema and row identity"
468 )]
469 fn bind_source_routines_for_storage(
470 &mut self,
471 engine: &dyn RoutineResolution,
472 source: &mut SourcePlan,
473 subqueries: &[QueryPlan],
474 params: &[SQLParam],
475 outer: Option<&RowSchema>,
476 ) -> Result<(), SQLError> {
477 match source {
478 SourcePlan::Join {
479 left,
480 right,
481 on,
482 lateral,
483 ..
484 } => {
485 self.bind_source_routines_for_storage(engine, left, subqueries, params, outer)?;
486 let left_schema = self.bind_source(engine, left, subqueries, params, outer)?;
487 let implicit_lateral_function = matches!(
488 right.as_ref(),
489 SourcePlan::Function { .. } | SourcePlan::FunctionGroup { .. }
490 );
491 let right_scope = (*lateral || implicit_lateral_function)
492 .then(|| overlay_outer_schema(&left_schema, outer));
493 let right_outer = right_scope.as_ref().or(outer);
494 self.bind_source_routines_for_storage(
495 engine,
496 right,
497 subqueries,
498 params,
499 right_outer,
500 )?;
501 if let Some(on) = on {
502 let right_schema =
503 self.bind_source(engine, right, subqueries, params, right_outer)?;
504 let input = RowSchema::join(&left_schema, &right_schema, std::iter::empty());
505 let input = overlay_outer_schema(&input, outer);
506 self.bind_scalar_routines_for_storage(
507 engine, on, &input, subqueries, params, outer,
508 )?;
509 }
510 Ok(())
511 }
512 SourcePlan::Subquery { body, .. } => self
513 .bind_query_routines_for_storage(engine, body, params, outer)
514 .map(|_| ()),
515 SourcePlan::Values { rows, .. } => {
516 let input = outer.cloned().unwrap_or_default();
517 for expression in rows.iter_mut().flatten() {
518 self.bind_scalar_routines_for_storage(
519 engine, expression, &input, subqueries, params, outer,
520 )?;
521 }
522 Ok(())
523 }
524 SourcePlan::Function {
525 name,
526 relations,
527 args,
528 ..
529 } => {
530 let local = crate::semantics::builtin_function_dispatch_name(name);
531 if crate::registry::is_operator_join_table_function(&local) {
532 let (left, right) = operator_join_relation_schemas(
533 &self.catalog,
534 &self.resolution,
535 relations.as_ref(),
536 )?;
537 let constant = RowSchema::default();
538 for (position, expression) in args.iter_mut().enumerate() {
539 let input = match position {
540 0 => &left,
541 1 => &right,
542 _ => &constant,
543 };
544 self.bind_scalar_routines_for_storage(
545 engine, expression, input, subqueries, params, outer,
546 )?;
547 }
548 return Ok(());
549 }
550 let input = outer.cloned().unwrap_or_default();
551 for expression in args {
552 self.bind_scalar_routines_for_storage(
553 engine, expression, &input, subqueries, params, outer,
554 )?;
555 }
556 Ok(())
557 }
558 SourcePlan::FunctionGroup { functions, .. } => {
559 for function in functions {
560 let local = crate::semantics::builtin_function_dispatch_name(&function.name);
561 if crate::registry::is_operator_join_table_function(&local) {
562 let (left, right) = operator_join_relation_schemas(
563 &self.catalog,
564 &self.resolution,
565 function.relations.as_ref(),
566 )?;
567 let constant = RowSchema::default();
568 for (position, expression) in function.args.iter_mut().enumerate() {
569 let input = match position {
570 0 => &left,
571 1 => &right,
572 _ => &constant,
573 };
574 self.bind_scalar_routines_for_storage(
575 engine, expression, input, subqueries, params, outer,
576 )?;
577 }
578 continue;
579 }
580 let input = outer.cloned().unwrap_or_default();
581 for expression in &mut function.args {
582 self.bind_scalar_routines_for_storage(
583 engine, expression, &input, subqueries, params, outer,
584 )?;
585 }
586 }
587 Ok(())
588 }
589 SourcePlan::Table { .. } => Ok(()),
590 }
591 }
592
593 fn bind_scalar_routines_for_storage(
594 &mut self,
595 engine: &dyn RoutineResolution,
596 expression: &mut ScalarExpr,
597 schema: &RowSchema,
598 subqueries: &[QueryPlan],
599 params: &[SQLParam],
600 outer: Option<&RowSchema>,
601 ) -> Result<(), SQLError> {
602 let schema = self.with_stored_outer_internal_aliases(schema);
603 let schema = &schema;
604 self.canonicalize_stored_outer_columns(expression, schema);
605 let mut failure = None;
606 crate::plan::rewrite_scalar_expression(expression, &mut |expression| {
607 if failure.is_some() {
608 return;
609 }
610 let ScalarExpr::Func {
611 name,
612 binding,
613 args,
614 ..
615 } = expression
616 else {
617 return;
618 };
619 if let Some(dispatch) = binding.as_ref().and_then(|binding| binding.dispatch) {
620 if let crate::ast::FunctionDispatch::NumericOperator(operator) = dispatch {
621 let selected = (|| {
622 let resolver = self.query_function_type_resolver_for_subqueries(
623 engine, args, schema, subqueries, params, outer,
624 )?;
625 let (_, types, _) = crate::function_call_argument_signature(
626 args,
627 schema,
628 params,
629 Some(&resolver),
630 )?;
631 crate::type_resolution::numeric_operator_types(operator, &types)
632 })();
633 match selected {
634 Ok(selected) => {
635 binding
636 .as_mut()
637 .expect("structural operator binding")
638 .argument_types = selected
639 .arguments
640 .iter()
641 .map(crate::ColumnType::sql_name)
642 .collect();
643 }
644 Err(error) => failure = Some(error),
645 }
646 }
647 return;
648 }
649 if let Err(error) = self.bind_scalar_function_for_storage(
650 engine, name, binding, args, schema, subqueries, params, outer,
651 ) {
652 failure = Some(error);
653 }
654 });
655 failure.map_or(Ok(()), Err)
656 }
657
658 #[expect(
659 clippy::too_many_arguments,
660 reason = "keeps execution context inputs aligned"
661 )]
662 fn bind_scalar_function_for_storage(
663 &mut self,
664 engine: &dyn RoutineResolution,
665 name: &str,
666 binding: &mut Option<FunctionBinding>,
667 args: &[ScalarExpr],
668 schema: &RowSchema,
669 subqueries: &[QueryPlan],
670 params: &[SQLParam],
671 outer: Option<&RowSchema>,
672 ) -> Result<(), SQLError> {
673 let resolver = self.query_function_type_resolver_for_subqueries(
674 engine, args, schema, subqueries, params, outer,
675 )?;
676 let (argument_names, argument_types, explicit_variadic) =
677 crate::function_call_argument_signature(args, schema, params, Some(&resolver))?;
678 let selected = if crate::is_fixed_builtin(name) {
679 crate::resolve_fixed_builtin_call(
680 name,
681 binding.as_ref(),
682 &argument_names,
683 &argument_types,
684 explicit_variadic,
685 Some(&resolver),
686 )?
687 .map(|resolved| resolved.selected)
688 } else {
689 resolver.resolve_function_overload(
690 name,
691 binding.as_ref(),
692 &argument_names,
693 &argument_types,
694 explicit_variadic,
695 )?
696 };
697 if let Some(selected) = selected {
698 *binding = Some(selected.binding);
699 }
700 Ok(())
701 }
702}
703
704pub fn bind_query_plan_routines_for_storage(
705 engine: &dyn RoutineResolution,
706 plan: &mut QueryPlan,
707 params: &[SQLParam],
708 ctes: &BindingContext,
709 outer: Option<&RowSchema>,
710) -> Result<RowSchema, SQLError> {
711 SchemaScope::for_analysis(ctes)?.bind_query_routines_for_storage(engine, plan, params, outer)
712}
713
714pub fn bind_expression_plan_routines_for_storage(
716 engine: &dyn RoutineResolution,
717 plan: &mut ExpressionPlan,
718 params: &[SQLParam],
719 ctes: &BindingContext,
720 schema: &RowSchema,
721) -> Result<Option<ColumnType>, SQLError> {
722 let mut scope = SchemaScope::for_analysis(ctes)?;
723 scope.stored_expression_outer = Some(schema.clone());
724 for subquery in &mut plan.subqueries {
725 scope.bind_query_routines_for_storage(engine, subquery, params, Some(schema))?;
726 }
727 scope.bind_scalar_routines_for_storage(
728 engine,
729 &mut plan.scalar,
730 schema,
731 &plan.subqueries,
732 params,
733 None,
734 )?;
735 scope.bind_expression_type(engine, &plan.scalar, schema, &plan.subqueries, params, None)
736}