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 for expression in &mut block.group_by {
344 self.bind_scalar_routines_for_storage(
345 routines,
346 expression,
347 &expression_schema,
348 &block.subqueries,
349 params,
350 outer,
351 )?;
352 }
353 for set in &mut block.grouping_sets {
354 for expression in set {
355 self.bind_scalar_routines_for_storage(
356 routines,
357 expression,
358 &expression_schema,
359 &block.subqueries,
360 params,
361 outer,
362 )?;
363 }
364 }
365 if let Some(having) = block.having.as_mut() {
366 self.bind_scalar_routines_for_storage(
367 routines,
368 having,
369 &expression_schema,
370 &block.subqueries,
371 params,
372 outer,
373 )?;
374 }
375 for order in &mut block.order_by {
376 self.bind_scalar_routines_for_storage(
377 routines,
378 &mut order.expr,
379 &expression_schema,
380 &block.subqueries,
381 params,
382 outer,
383 )?;
384 }
385 if let Some(limit) = block.limit.as_mut() {
386 self.bind_scalar_routines_for_storage(
387 routines,
388 limit,
389 &expression_schema,
390 &block.subqueries,
391 params,
392 outer,
393 )?;
394 }
395 if let Some(offset) = block.offset.as_mut() {
396 self.bind_scalar_routines_for_storage(
397 routines,
398 offset,
399 &expression_schema,
400 &block.subqueries,
401 params,
402 outer,
403 )?;
404 }
405 for expression in &mut block.distinct_on {
406 self.bind_scalar_routines_for_storage(
407 routines,
408 expression,
409 &expression_schema,
410 &block.subqueries,
411 params,
412 outer,
413 )?;
414 }
415 }
416 RelationalPlan::SetOp {
417 order_by,
418 limit,
419 offset,
420 subqueries,
421 ..
422 } => {
423 let output = set_output
424 .as_ref()
425 .expect("set-operation output schema was bound before routine expressions");
426 for order in order_by {
427 self.bind_scalar_routines_for_storage(
428 routines,
429 &mut order.expr,
430 output,
431 subqueries,
432 params,
433 outer,
434 )?;
435 }
436 if let Some(limit) = limit {
437 self.bind_scalar_routines_for_storage(
438 routines, limit, output, subqueries, params, outer,
439 )?;
440 }
441 if let Some(offset) = offset {
442 self.bind_scalar_routines_for_storage(
443 routines, offset, output, subqueries, params, outer,
444 )?;
445 }
446 }
447 RelationalPlan::Values { rows, subqueries } => {
448 let input = outer.cloned().unwrap_or_default();
449 for expression in rows.iter_mut().flatten() {
450 self.bind_scalar_routines_for_storage(
451 routines, expression, &input, subqueries, params, outer,
452 )?;
453 }
454 }
455 }
456 self.bind_root(routines, root, params, outer, false)
457 }
458
459 #[expect(
460 clippy::too_many_lines,
461 reason = "preserves SELECT schema and row identity"
462 )]
463 fn bind_source_routines_for_storage(
464 &mut self,
465 engine: &dyn RoutineResolution,
466 source: &mut SourcePlan,
467 subqueries: &[QueryPlan],
468 params: &[SQLParam],
469 outer: Option<&RowSchema>,
470 ) -> Result<(), SQLError> {
471 match source {
472 SourcePlan::Join {
473 left,
474 right,
475 on,
476 lateral,
477 ..
478 } => {
479 self.bind_source_routines_for_storage(engine, left, subqueries, params, outer)?;
480 let left_schema = self.bind_source(engine, left, subqueries, params, outer)?;
481 let implicit_lateral_function = matches!(
482 right.as_ref(),
483 SourcePlan::Function { .. } | SourcePlan::FunctionGroup { .. }
484 );
485 let right_scope = (*lateral || implicit_lateral_function)
486 .then(|| overlay_outer_schema(&left_schema, outer));
487 let right_outer = right_scope.as_ref().or(outer);
488 self.bind_source_routines_for_storage(
489 engine,
490 right,
491 subqueries,
492 params,
493 right_outer,
494 )?;
495 if let Some(on) = on {
496 let right_schema =
497 self.bind_source(engine, right, subqueries, params, right_outer)?;
498 let input = RowSchema::join(&left_schema, &right_schema, std::iter::empty());
499 let input = overlay_outer_schema(&input, outer);
500 self.bind_scalar_routines_for_storage(
501 engine, on, &input, subqueries, params, outer,
502 )?;
503 }
504 Ok(())
505 }
506 SourcePlan::Subquery { body, .. } => self
507 .bind_query_routines_for_storage(engine, body, params, outer)
508 .map(|_| ()),
509 SourcePlan::Values { rows, .. } => {
510 let input = outer.cloned().unwrap_or_default();
511 for expression in rows.iter_mut().flatten() {
512 self.bind_scalar_routines_for_storage(
513 engine, expression, &input, subqueries, params, outer,
514 )?;
515 }
516 Ok(())
517 }
518 SourcePlan::Function {
519 name,
520 relations,
521 args,
522 ..
523 } => {
524 let local = crate::semantics::builtin_function_dispatch_name(name);
525 if crate::registry::is_operator_join_table_function(&local) {
526 let (left, right) = operator_join_relation_schemas(
527 &self.catalog,
528 &self.resolution,
529 relations.as_ref(),
530 )?;
531 let constant = RowSchema::default();
532 for (position, expression) in args.iter_mut().enumerate() {
533 let input = match position {
534 0 => &left,
535 1 => &right,
536 _ => &constant,
537 };
538 self.bind_scalar_routines_for_storage(
539 engine, expression, input, subqueries, params, outer,
540 )?;
541 }
542 return Ok(());
543 }
544 let input = outer.cloned().unwrap_or_default();
545 for expression in args {
546 self.bind_scalar_routines_for_storage(
547 engine, expression, &input, subqueries, params, outer,
548 )?;
549 }
550 Ok(())
551 }
552 SourcePlan::FunctionGroup { functions, .. } => {
553 for function in functions {
554 let local = crate::semantics::builtin_function_dispatch_name(&function.name);
555 if crate::registry::is_operator_join_table_function(&local) {
556 let (left, right) = operator_join_relation_schemas(
557 &self.catalog,
558 &self.resolution,
559 function.relations.as_ref(),
560 )?;
561 let constant = RowSchema::default();
562 for (position, expression) in function.args.iter_mut().enumerate() {
563 let input = match position {
564 0 => &left,
565 1 => &right,
566 _ => &constant,
567 };
568 self.bind_scalar_routines_for_storage(
569 engine, expression, input, subqueries, params, outer,
570 )?;
571 }
572 continue;
573 }
574 let input = outer.cloned().unwrap_or_default();
575 for expression in &mut function.args {
576 self.bind_scalar_routines_for_storage(
577 engine, expression, &input, subqueries, params, outer,
578 )?;
579 }
580 }
581 Ok(())
582 }
583 SourcePlan::Table { .. } => Ok(()),
584 }
585 }
586
587 fn bind_scalar_routines_for_storage(
588 &mut self,
589 engine: &dyn RoutineResolution,
590 expression: &mut ScalarExpr,
591 schema: &RowSchema,
592 subqueries: &[QueryPlan],
593 params: &[SQLParam],
594 outer: Option<&RowSchema>,
595 ) -> Result<(), SQLError> {
596 let schema = self.with_stored_outer_internal_aliases(schema);
597 let schema = &schema;
598 self.canonicalize_stored_outer_columns(expression, schema);
599 let mut failure = None;
600 crate::plan::rewrite_scalar_expression(expression, &mut |expression| {
601 if failure.is_some() {
602 return;
603 }
604 let ScalarExpr::Func {
605 name,
606 binding,
607 args,
608 ..
609 } = expression
610 else {
611 return;
612 };
613 if binding
614 .as_ref()
615 .and_then(|binding| binding.dispatch)
616 .is_some()
617 {
618 return;
619 }
620 if let Err(error) = self.bind_scalar_function_for_storage(
621 engine, name, binding, args, schema, subqueries, params, outer,
622 ) {
623 failure = Some(error);
624 }
625 });
626 failure.map_or(Ok(()), Err)
627 }
628
629 #[expect(
630 clippy::too_many_arguments,
631 reason = "keeps execution context inputs aligned"
632 )]
633 fn bind_scalar_function_for_storage(
634 &mut self,
635 engine: &dyn RoutineResolution,
636 name: &str,
637 binding: &mut Option<FunctionBinding>,
638 args: &[ScalarExpr],
639 schema: &RowSchema,
640 subqueries: &[QueryPlan],
641 params: &[SQLParam],
642 outer: Option<&RowSchema>,
643 ) -> Result<(), SQLError> {
644 let resolver = self.query_function_type_resolver_for_subqueries(
645 engine, args, schema, subqueries, params, outer,
646 )?;
647 let (argument_names, argument_types, explicit_variadic) =
648 crate::function_call_argument_signature(args, schema, params, Some(&resolver))?;
649 let selected = if crate::is_fixed_builtin(name) {
650 crate::resolve_fixed_builtin_call(
651 name,
652 binding.as_ref(),
653 &argument_names,
654 &argument_types,
655 explicit_variadic,
656 Some(&resolver),
657 )?
658 .map(|resolved| resolved.selected)
659 } else {
660 resolver.resolve_function_overload(
661 name,
662 binding.as_ref(),
663 &argument_names,
664 &argument_types,
665 explicit_variadic,
666 )?
667 };
668 if let Some(selected) = selected {
669 *binding = Some(selected.binding);
670 }
671 Ok(())
672 }
673}
674
675pub fn bind_query_plan_routines_for_storage(
676 engine: &dyn RoutineResolution,
677 plan: &mut QueryPlan,
678 params: &[SQLParam],
679 ctes: &BindingContext,
680 outer: Option<&RowSchema>,
681) -> Result<RowSchema, SQLError> {
682 SchemaScope::for_analysis(ctes)?.bind_query_routines_for_storage(engine, plan, params, outer)
683}
684
685pub fn bind_expression_plan_routines_for_storage(
687 engine: &dyn RoutineResolution,
688 plan: &mut ExpressionPlan,
689 params: &[SQLParam],
690 ctes: &BindingContext,
691 schema: &RowSchema,
692) -> Result<Option<ColumnType>, SQLError> {
693 let mut scope = SchemaScope::for_analysis(ctes)?;
694 scope.stored_expression_outer = Some(schema.clone());
695 for subquery in &mut plan.subqueries {
696 scope.bind_query_routines_for_storage(engine, subquery, params, Some(schema))?;
697 }
698 scope.bind_scalar_routines_for_storage(
699 engine,
700 &mut plan.scalar,
701 schema,
702 &plan.subqueries,
703 params,
704 None,
705 )?;
706 scope.bind_expression_type(engine, &plan.scalar, schema, &plan.subqueries, params, None)
707}