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 self.bind_command_routines_for_storage(routines, command, params, outer)?;
100 self.bind_command_returning(routines, command, params)
101 }
102
103 pub(super) fn bind_cte_routine_schemas(
104 &mut self,
105 routines: &dyn RoutineResolution,
106 ctes: &mut [crate::plan::CtePlan],
107 params: &[SQLParam],
108 outer: Option<&RowSchema>,
109 ) -> Result<Vec<(String, bool, Option<RowSchema>)>, SQLError> {
110 let ordered_names = crate::semantics::ordered_cte_plans(ctes)?
111 .into_iter()
112 .map(|cte| cte.name.clone())
113 .collect::<Vec<_>>();
114 let mut previous = Vec::with_capacity(ordered_names.len());
115 for name in ordered_names {
116 let position = ctes
117 .iter()
118 .position(|cte| cte.name == name)
119 .ok_or_else(|| SQLError::Internal(format!("ordered CTE `{name}` disappeared")))?;
120 let self_recursive = cte_references_own_name(&ctes[position]);
121 if let Some(cycle) = ctes[position].cycle.as_mut() {
122 let schema = RowSchema::default();
123 self.bind_scalar_routines_for_storage(
124 routines,
125 &mut cycle.mark_value,
126 &schema,
127 &[],
128 params,
129 )?;
130 self.bind_scalar_routines_for_storage(
131 routines,
132 &mut cycle.mark_default,
133 &schema,
134 &[],
135 params,
136 )?;
137 }
138 let provisional = if self_recursive {
139 self.bind_recursive_seed(
140 routines,
141 ctes[position]
142 .body
143 .query()
144 .ok_or_else(|| SQLError::Routine {
145 sqlstate: "42P19".into(),
146 message: format!(
147 "recursive query \"{}\" must not contain data-modifying statements",
148 ctes[position].name
149 ),
150 })?,
151 params,
152 outer,
153 )?
154 } else {
155 self.bind_cte_routines_for_storage(
156 routines,
157 &mut ctes[position].body,
158 params,
159 outer,
160 )?
161 };
162 let columns = ctes[position].columns.clone();
163 let provisional = rename_schema(&provisional, &columns, None);
164 let provisional = if self_recursive {
165 extend_recursive_cte_binding_schema(routines, &ctes[position], provisional, params)?
166 } else {
167 extend_cte_generated_schema(routines, &ctes[position], provisional, params)?
168 };
169 previous.push((
170 name.clone(),
171 self.set_cte_returning(&ctes[position]),
172 self.ctes.insert(name.clone(), provisional),
173 ));
174 if self_recursive {
175 let complete = self.bind_cte_routines_for_storage(
176 routines,
177 &mut ctes[position].body,
178 params,
179 outer,
180 )?;
181 let complete = rename_schema(&complete, &columns, None);
182 let complete =
183 extend_cte_generated_schema(routines, &ctes[position], complete, params)?;
184 self.ctes.insert(name, complete);
185 }
186 }
187
188 Ok(previous)
189 }
190
191 pub(super) fn bind_query_routines_for_storage(
192 &mut self,
193 routines: &dyn RoutineResolution,
194 plan: &mut QueryPlan,
195 params: &[SQLParam],
196 outer: Option<&RowSchema>,
197 ) -> Result<RowSchema, SQLError> {
198 let previous = self.bind_cte_routine_schemas(routines, &mut plan.ctes, params, outer)?;
199
200 let result = self.bind_root_routines_for_storage(routines, &mut plan.root, params, outer);
201 self.restore_cte_schemas(previous);
202 result
203 }
204
205 #[expect(
206 clippy::too_many_lines,
207 reason = "preserves SELECT schema and row identity"
208 )]
209 fn bind_root_routines_for_storage(
210 &mut self,
211 routines: &dyn RoutineResolution,
212 root: &mut RelationalPlan,
213 params: &[SQLParam],
214 outer: Option<&RowSchema>,
215 ) -> Result<RowSchema, SQLError> {
216 match root {
217 RelationalPlan::QueryBlock(block) => {
218 if let Some(source) = block.from.as_mut() {
220 self.bind_source_routines_for_storage(
221 routines,
222 source,
223 &block.subqueries,
224 params,
225 outer,
226 )?;
227 }
228 let source_schema = match block.from.as_mut() {
229 Some(source) => self.bind_source_for_execution(
230 routines,
231 source,
232 &block.subqueries,
233 params,
234 outer,
235 )?,
236 None => RowSchema::default(),
237 };
238 let expression_schema = overlay_outer_schema(&source_schema, outer);
239 for subquery in &mut block.subqueries {
240 self.bind_query_routines_for_storage(
241 routines,
242 subquery,
243 params,
244 Some(&expression_schema),
245 )?;
246 }
247 }
248 RelationalPlan::SetOp {
249 left,
250 right,
251 subqueries,
252 ..
253 } => {
254 self.bind_query_routines_for_storage(routines, left, params, outer)?;
255 self.bind_query_routines_for_storage(routines, right, params, outer)?;
256 for subquery in subqueries {
257 self.bind_query_routines_for_storage(routines, subquery, params, outer)?;
258 }
259 }
260 RelationalPlan::Values { subqueries, .. } => {
261 for subquery in subqueries {
262 self.bind_query_routines_for_storage(routines, subquery, params, outer)?;
263 }
264 }
265 }
266
267 let set_output = match &*root {
268 RelationalPlan::SetOp { .. } => {
269 Some(self.bind_root(routines, root, params, outer, false)?)
270 }
271 RelationalPlan::QueryBlock(_) | RelationalPlan::Values { .. } => None,
272 };
273 match root {
274 RelationalPlan::QueryBlock(block) => {
275 if block.from.is_none()
276 && block
277 .projections
278 .iter()
279 .any(|projection| matches!(projection.expr, ScalarExpr::Star))
280 && outer.is_none()
281 {
282 return Err(SQLError::Routine {
283 sqlstate: "42601".into(),
284 message: "SELECT * with no tables specified is not valid".into(),
285 });
286 }
287 let source_schema = block.from.as_ref().map_or_else(
288 || Ok(RowSchema::default()),
289 |source| self.bind_source(routines, source, &block.subqueries, params, outer),
290 )?;
291 let expression_schema = overlay_outer_schema(&source_schema, outer);
292 if let Some(filter) = block.r#where.as_mut() {
293 self.bind_scalar_routines_for_storage(
294 routines,
295 filter,
296 &expression_schema,
297 &block.subqueries,
298 params,
299 )?;
300 }
301 let labels = super::routine_parameters::column_labels(&block.projections);
302 for projection in &mut block.projections {
303 self.bind_scalar_routines_for_storage(
304 routines,
305 &mut projection.expr,
306 &expression_schema,
307 &block.subqueries,
308 params,
309 )?;
310 }
311 super::routine_parameters::keep_column_labels(&mut block.projections, labels);
312 if self.preserve_syntax_shape {
313 let mut expanded = (**block).clone();
315 expanded.projections = crate::semantics::expand_bound_projection_stars(
316 &block.projections,
317 &source_schema,
318 )?;
319 self.bind_grouping_variable_sites(&mut expanded, &source_schema);
320 crate::semantics::grouping_sets::bind_grouping_names(
321 routines,
322 &mut expanded,
323 &source_schema,
324 None,
325 params,
326 )?;
327 } else {
328 block.projections = crate::semantics::expand_bound_projection_stars(
329 &block.projections,
330 &source_schema,
331 )?;
332 self.bind_grouping_variable_sites(block, &source_schema);
333 crate::semantics::grouping_sets::bind_grouping_names(
334 routines,
335 block,
336 &source_schema,
337 None,
338 params,
339 )?;
340 }
341 let output_names = self.output_names(&block.projections);
342 for expression in block
343 .group_by
344 .iter_mut()
345 .chain(block.grouping_sets.iter_mut().flatten())
346 {
347 if self.preserve_syntax_shape
349 && matches!(expression, ScalarExpr::Column(name)
350 if !source_schema.has_unqualified_column(name)
351 && !source_schema.column_is_ambiguous(name)
352 && output_names.contains(name))
353 {
354 continue;
355 }
356 self.bind_scalar_routines_for_storage(
357 routines,
358 expression,
359 &expression_schema,
360 &block.subqueries,
361 params,
362 )?;
363 }
364 if let Some(having) = block.having.as_mut() {
365 self.bind_scalar_routines_for_storage(
366 routines,
367 having,
368 &expression_schema,
369 &block.subqueries,
370 params,
371 )?;
372 }
373 let names_output = |expression: &ScalarExpr| match expression {
375 ScalarExpr::Column(name) => output_names.contains(name),
376 _ => false,
377 };
378 for order in &mut block.order_by {
379 if names_output(&order.expr)
380 || self.output_takes_variable_site(&order.expr, &output_names)
381 {
382 continue;
383 }
384 self.bind_scalar_routines_for_storage(
385 routines,
386 &mut order.expr,
387 &expression_schema,
388 &block.subqueries,
389 params,
390 )?;
391 }
392 if let Some(limit) = block.limit.as_mut() {
393 self.bind_scalar_routines_for_storage(
394 routines,
395 limit,
396 &expression_schema,
397 &block.subqueries,
398 params,
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 )?;
409 }
410 for expression in &mut block.distinct_on {
411 if names_output(expression)
412 || self.output_takes_variable_site(expression, &output_names)
413 {
414 continue;
415 }
416 self.bind_scalar_routines_for_storage(
417 routines,
418 expression,
419 &expression_schema,
420 &block.subqueries,
421 params,
422 )?;
423 }
424 for expression in block
425 .windows
426 .iter_mut()
427 .flat_map(|window| window.spec.expressions_mut())
428 {
429 self.bind_scalar_routines_for_storage(
430 routines,
431 expression,
432 &expression_schema,
433 &block.subqueries,
434 params,
435 )?;
436 }
437 }
438 RelationalPlan::SetOp {
439 order_by,
440 limit,
441 offset,
442 subqueries,
443 ..
444 } => {
445 let output = overlay_outer_schema(
446 set_output
447 .as_ref()
448 .expect("set-operation output schema was bound before routine expressions"),
449 outer,
450 );
451 let output = &output;
452 for order in order_by {
453 self.bind_scalar_routines_for_storage(
454 routines,
455 &mut order.expr,
456 output,
457 subqueries,
458 params,
459 )?;
460 }
461 if let Some(limit) = limit {
462 self.bind_scalar_routines_for_storage(
463 routines, limit, output, subqueries, params,
464 )?;
465 }
466 if let Some(offset) = offset {
467 self.bind_scalar_routines_for_storage(
468 routines, offset, output, subqueries, params,
469 )?;
470 }
471 }
472 RelationalPlan::Values { rows, subqueries } => {
473 let input = outer.cloned().unwrap_or_default();
474 for expression in rows.iter_mut().flatten() {
475 self.bind_scalar_routines_for_storage(
476 routines, expression, &input, subqueries, params,
477 )?;
478 }
479 }
480 }
481 self.bind_root(routines, root, params, outer, false)
482 }
483
484 #[expect(
485 clippy::too_many_lines,
486 reason = "preserves SELECT schema and row identity"
487 )]
488 pub(super) fn bind_source_routines_for_storage(
489 &mut self,
490 engine: &dyn RoutineResolution,
491 source: &mut SourcePlan,
492 subqueries: &[QueryPlan],
493 params: &[SQLParam],
494 outer: Option<&RowSchema>,
495 ) -> Result<(), SQLError> {
496 match source {
497 SourcePlan::Join {
498 left,
499 right,
500 on,
501 lateral,
502 ..
503 } => {
504 self.bind_source_routines_for_storage(engine, left, subqueries, params, outer)?;
505 let left_schema = self.bind_source(engine, left, subqueries, params, outer)?;
506 let implicit_lateral_function = matches!(
507 right.as_ref(),
508 SourcePlan::Function { .. } | SourcePlan::FunctionGroup { .. }
509 );
510 let right_scope = (*lateral || implicit_lateral_function)
511 .then(|| overlay_outer_schema(&left_schema, outer));
512 let right_outer = right_scope.as_ref().or(outer);
513 self.bind_source_routines_for_storage(
514 engine,
515 right,
516 subqueries,
517 params,
518 right_outer,
519 )?;
520 if let Some(on) = on {
521 let right_schema =
522 self.bind_source(engine, right, subqueries, params, right_outer)?;
523 let input = RowSchema::join(&left_schema, &right_schema, std::iter::empty());
524 let input = overlay_outer_schema(&input, outer);
525 self.bind_scalar_routines_for_storage(engine, on, &input, subqueries, params)?;
526 }
527 Ok(())
528 }
529 SourcePlan::Subquery { body, .. } => self
530 .bind_query_routines_for_storage(engine, body, params, outer)
531 .map(|_| ()),
532 SourcePlan::Values { rows, .. } => {
533 let input = outer.cloned().unwrap_or_default();
534 for expression in rows.iter_mut().flatten() {
535 self.bind_scalar_routines_for_storage(
536 engine, expression, &input, subqueries, params,
537 )?;
538 }
539 Ok(())
540 }
541 SourcePlan::Function {
542 name,
543 relations,
544 args,
545 ..
546 } => {
547 let local = crate::semantics::builtin_function_dispatch_name(name);
548 if crate::registry::is_operator_join_table_function(&local) {
549 let (left, right) = operator_join_relation_schemas(
550 &self.catalog,
551 &self.resolution,
552 relations.as_ref(),
553 )?;
554 let constant = RowSchema::default();
555 for (position, expression) in args.iter_mut().enumerate() {
556 let input = match position {
557 0 => &left,
558 1 => &right,
559 _ => &constant,
560 };
561 self.bind_scalar_routines_for_storage(
562 engine, expression, input, subqueries, params,
563 )?;
564 }
565 return Ok(());
566 }
567 let input = outer.cloned().unwrap_or_default();
568 for expression in args {
569 self.bind_scalar_routines_for_storage(
570 engine, expression, &input, subqueries, params,
571 )?;
572 }
573 Ok(())
574 }
575 SourcePlan::FunctionGroup { functions, .. } => {
576 for function in functions {
577 let local = crate::semantics::builtin_function_dispatch_name(&function.name);
578 if crate::registry::is_operator_join_table_function(&local) {
579 let (left, right) = operator_join_relation_schemas(
580 &self.catalog,
581 &self.resolution,
582 function.relations.as_ref(),
583 )?;
584 let constant = RowSchema::default();
585 for (position, expression) in function.args.iter_mut().enumerate() {
586 let input = match position {
587 0 => &left,
588 1 => &right,
589 _ => &constant,
590 };
591 self.bind_scalar_routines_for_storage(
592 engine, expression, input, subqueries, params,
593 )?;
594 }
595 continue;
596 }
597 let input = outer.cloned().unwrap_or_default();
598 for expression in &mut function.args {
599 self.bind_scalar_routines_for_storage(
600 engine, expression, &input, subqueries, params,
601 )?;
602 }
603 }
604 Ok(())
605 }
606 SourcePlan::Table { .. } => Ok(()),
607 }
608 }
609
610 pub(super) fn bind_scalar_routines_for_storage(
611 &mut self,
612 engine: &dyn RoutineResolution,
613 expression: &mut ScalarExpr,
614 schema: &RowSchema,
615 subqueries: &[QueryPlan],
616 params: &[SQLParam],
617 ) -> Result<(), SQLError> {
618 let schema = self.with_stored_outer_internal_aliases(schema);
619 let schema = &schema;
620 self.canonicalize_stored_outer_columns(expression, schema);
621 self.canonicalize_routine_parameters(expression, schema);
622 self.resolve_variable_sites(expression, schema);
623 if !self.binds_routine_identities {
624 return Ok(());
625 }
626 let mut failure = None;
627 crate::plan::rewrite_scalar_expression(expression, &mut |expression| {
628 if failure.is_some() {
629 return;
630 }
631 if matches!(expression, ScalarExpr::Func { order_syntax, name, binding, order_by, .. }
632 if super::ordered_calls::uses_ordered_arguments(*order_syntax, name, binding.as_ref(), order_by.len())
633 || super::ordered_calls::is_ordered_set(name))
634 {
635 if let Err(error) = self.bind_ordered_function_for_storage(
636 engine, expression, schema, subqueries, params,
637 ) {
638 failure = Some(error);
639 }
640 return;
641 }
642 let ScalarExpr::Func {
643 name,
644 binding,
645 args,
646 ..
647 } = expression
648 else {
649 return;
650 };
651 if let Some(dispatch) = binding.as_ref().and_then(|binding| binding.dispatch) {
652 if let crate::ast::FunctionDispatch::NumericOperator(operator) = dispatch {
653 let selected = (|| {
654 let resolver = self.query_function_type_resolver_for_subqueries(
655 engine, args, schema, subqueries, params,
656 )?;
657 let (_, types, _) = crate::function_call_argument_signature(
658 args,
659 schema,
660 params,
661 Some(&resolver),
662 )?;
663 crate::type_resolution::numeric_operator_types(operator, &types)
664 })();
665 match selected {
666 Ok(selected) => {
667 binding
668 .as_mut()
669 .expect("structural operator binding")
670 .argument_types = selected
671 .arguments
672 .iter()
673 .map(crate::ColumnType::sql_name)
674 .collect();
675 }
676 Err(error) => failure = Some(error),
677 }
678 }
679 return;
680 }
681 if let Err(error) = self.bind_scalar_function_for_storage(
682 engine, name, binding, args, schema, subqueries, params,
683 ) {
684 failure = Some(error);
685 }
686 });
687 failure.map_or(Ok(()), Err)?;
688 self.bind_stored_scalar_types(engine, expression, schema, subqueries, params)
689 }
690
691 fn bind_stored_scalar_types(
693 &mut self,
694 engine: &dyn RoutineResolution,
695 expression: &mut ScalarExpr,
696 schema: &RowSchema,
697 subqueries: &[QueryPlan],
698 params: &[SQLParam],
699 ) -> Result<(), SQLError> {
700 super::stored_types::bind_scalar_type_identities(expression, &mut |name| {
701 engine.resolve_type_name(name)
702 })?;
703 let resolver = self.query_function_type_resolver_for_subqueries(
704 engine,
705 std::slice::from_ref(expression),
706 schema,
707 subqueries,
708 params,
709 )?;
710 crate::type_resolution::store_operand_coercions(expression, schema, params, &resolver)?;
712 if !crate::type_resolution::contains_unknown_literal(expression) {
713 return Ok(());
714 }
715 crate::type_resolution::fold_stored_enum_constants(expression, schema, params, &resolver)
716 .map(drop)
717 }
718
719 #[expect(
720 clippy::too_many_arguments,
721 reason = "keeps execution context inputs aligned"
722 )]
723 fn bind_scalar_function_for_storage(
724 &mut self,
725 engine: &dyn RoutineResolution,
726 name: &str,
727 binding: &mut Option<FunctionBinding>,
728 args: &[ScalarExpr],
729 schema: &RowSchema,
730 subqueries: &[QueryPlan],
731 params: &[SQLParam],
732 ) -> Result<(), SQLError> {
733 let resolver = self.query_function_type_resolver_for_subqueries(
734 engine, args, schema, subqueries, params,
735 )?;
736 let (argument_names, argument_types, explicit_variadic) =
737 crate::function_call_argument_signature(args, schema, params, Some(&resolver))?;
738 let selected = if crate::is_fixed_builtin(name) {
739 crate::resolve_fixed_builtin_call(
740 name,
741 binding.as_ref(),
742 &argument_names,
743 &argument_types,
744 explicit_variadic,
745 Some(&resolver),
746 )?
747 .map(|resolved| resolved.selected)
748 } else if let Some(array) = crate::type_resolution::resolve_array_transform_call(
749 name,
750 binding.as_ref(),
751 args,
752 &argument_types,
753 explicit_variadic,
754 &resolver,
755 )? {
756 array.overload
757 } else {
758 resolver.resolve_function_overload(
759 name,
760 binding.as_ref(),
761 &argument_names,
762 &argument_types,
763 explicit_variadic,
764 )?
765 };
766 if let Some(selected) = selected {
767 *binding = Some(selected.binding);
768 }
769 Ok(())
770 }
771}
772
773pub fn bind_query_plan_routines_for_storage(
774 engine: &dyn RoutineResolution,
775 plan: &mut QueryPlan,
776 params: &[SQLParam],
777 ctes: &BindingContext,
778 outer: Option<&RowSchema>,
779) -> Result<RowSchema, SQLError> {
780 SchemaScope::for_analysis(ctes)?.bind_query_routines_for_storage(engine, plan, params, outer)
781}
782
783pub fn bind_syntax_query_plan_routines(
785 engine: &dyn RoutineResolution,
786 plan: &mut QueryPlan,
787 params: &[SQLParam],
788 ctes: &BindingContext,
789 outer: Option<&RowSchema>,
790) -> Result<RowSchema, SQLError> {
791 let mut scope = SchemaScope::for_analysis(ctes)?;
792 scope.preserve_syntax_shape = true;
793 scope.bind_query_routines_for_storage(engine, plan, params, outer)
794}
795
796pub fn bind_expression_plan_routines_for_storage(
798 engine: &dyn RoutineResolution,
799 plan: &mut ExpressionPlan,
800 params: &[SQLParam],
801 ctes: &BindingContext,
802 schema: &RowSchema,
803) -> Result<Option<ColumnType>, SQLError> {
804 let mut scope = SchemaScope::for_analysis(ctes)?;
805 scope.preserve_syntax_shape = true;
806 scope.stored_expression_outer = Some(schema.clone());
807 for subquery in &mut plan.subqueries {
808 scope.bind_query_routines_for_storage(engine, subquery, params, Some(schema))?;
809 }
810 scope.bind_scalar_routines_for_storage(
811 engine,
812 &mut plan.scalar,
813 schema,
814 &plan.subqueries,
815 params,
816 )?;
817 scope.bind_expression_type(engine, &plan.scalar, schema, &plan.subqueries, params)
818}