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 outer_slot = slot - outer_start;
56 let Some(column) = outer.unique_internal_column_for_slot(outer_slot) else {
57 return;
58 };
59 *node = ScalarExpr::InternalColumn(column);
60 });
61 }
62
63 fn bind_cte_routines_for_storage(
64 &mut self,
65 routines: &dyn RoutineResolution,
66 body: &mut crate::plan::CtePlanBody,
67 params: &[SQLParam],
68 outer: Option<&RowSchema>,
69 ) -> Result<RowSchema, SQLError> {
70 let crate::plan::CtePlanBody::Command(command) = body else {
71 return self.bind_query_routines_for_storage(
72 routines,
73 body.query_mut().expect("query CTE body"),
74 params,
75 outer,
76 );
77 };
78 self.bind_command_routines_for_storage(routines, command, params, outer)?;
79 self.bind_command_returning(routines, command, params)
80 }
81
82 pub(super) fn bind_cte_routine_schemas(
83 &mut self,
84 routines: &dyn RoutineResolution,
85 ctes: &mut [crate::plan::CtePlan],
86 params: &[SQLParam],
87 outer: Option<&RowSchema>,
88 ) -> Result<Vec<(String, bool, Option<RowSchema>)>, SQLError> {
89 let ordered_names = crate::semantics::ordered_cte_plans(ctes)?
90 .into_iter()
91 .map(|cte| cte.name.clone())
92 .collect::<Vec<_>>();
93 let mut previous = Vec::with_capacity(ordered_names.len());
94 for name in ordered_names {
95 let position = ctes
96 .iter()
97 .position(|cte| cte.name == name)
98 .ok_or_else(|| SQLError::Internal(format!("ordered CTE `{name}` disappeared")))?;
99 let self_recursive = cte_references_own_name(&ctes[position]);
100 if let Some(cycle) = ctes[position].cycle.as_mut() {
101 let schema = RowSchema::default();
102 self.bind_scalar_routines_for_storage(
103 routines,
104 &mut cycle.mark_value,
105 &schema,
106 &[],
107 params,
108 )?;
109 self.bind_scalar_routines_for_storage(
110 routines,
111 &mut cycle.mark_default,
112 &schema,
113 &[],
114 params,
115 )?;
116 }
117 let provisional = if self_recursive {
118 self.bind_recursive_seed(
119 routines,
120 ctes[position]
121 .body
122 .query()
123 .ok_or_else(|| SQLError::Routine {
124 sqlstate: "42P19".into(),
125 message: format!(
126 "recursive query \"{}\" must not contain data-modifying statements",
127 ctes[position].name
128 ),
129 })?,
130 params,
131 outer,
132 )?
133 } else {
134 self.bind_cte_routines_for_storage(
135 routines,
136 &mut ctes[position].body,
137 params,
138 outer,
139 )?
140 };
141 let columns = ctes[position].columns.clone();
142 let provisional = rename_schema(&provisional, &columns, None);
143 let provisional = if self_recursive {
144 extend_recursive_cte_binding_schema(routines, &ctes[position], provisional, params)?
145 } else {
146 extend_cte_generated_schema(routines, &ctes[position], provisional, params)?
147 };
148 previous.push((
149 name.clone(),
150 self.set_cte_returning(&ctes[position]),
151 self.ctes.insert(name.clone(), provisional),
152 ));
153 if self_recursive {
154 let complete = self.bind_cte_routines_for_storage(
155 routines,
156 &mut ctes[position].body,
157 params,
158 outer,
159 )?;
160 let complete = rename_schema(&complete, &columns, None);
161 let complete =
162 extend_cte_generated_schema(routines, &ctes[position], complete, params)?;
163 self.ctes.insert(name, complete);
164 }
165 }
166
167 Ok(previous)
168 }
169
170 pub(super) fn bind_query_routines_for_storage(
171 &mut self,
172 routines: &dyn RoutineResolution,
173 plan: &mut QueryPlan,
174 params: &[SQLParam],
175 outer: Option<&RowSchema>,
176 ) -> Result<RowSchema, SQLError> {
177 let previous = self.bind_cte_routine_schemas(routines, &mut plan.ctes, params, outer)?;
178
179 let result = self.bind_root_routines_for_storage(routines, &mut plan.root, params, outer);
180 self.restore_cte_schemas(previous);
181 result
182 }
183
184 #[expect(
185 clippy::too_many_lines,
186 reason = "preserves SELECT schema and row identity"
187 )]
188 fn bind_root_routines_for_storage(
189 &mut self,
190 routines: &dyn RoutineResolution,
191 root: &mut RelationalPlan,
192 params: &[SQLParam],
193 outer: Option<&RowSchema>,
194 ) -> Result<RowSchema, SQLError> {
195 match root {
196 RelationalPlan::QueryBlock(block) => {
197 if let Some(source) = block.from.as_mut() {
199 self.bind_source_routines_for_storage(
200 routines,
201 source,
202 &block.subqueries,
203 params,
204 outer,
205 )?;
206 }
207 let source_schema = match block.from.as_mut() {
208 Some(source) => self.bind_source_for_execution(
209 routines,
210 source,
211 &block.subqueries,
212 params,
213 outer,
214 )?,
215 None => RowSchema::default(),
216 };
217 let expression_schema = overlay_outer_schema(&source_schema, outer);
218 for subquery in &mut block.subqueries {
219 self.bind_query_routines_for_storage(
220 routines,
221 subquery,
222 params,
223 Some(&expression_schema),
224 )?;
225 }
226 }
227 RelationalPlan::SetOp {
228 left,
229 right,
230 subqueries,
231 ..
232 } => {
233 self.bind_query_routines_for_storage(routines, left, params, outer)?;
234 self.bind_query_routines_for_storage(routines, right, params, outer)?;
235 for subquery in subqueries {
236 self.bind_query_routines_for_storage(routines, subquery, params, outer)?;
237 }
238 }
239 RelationalPlan::Values { subqueries, .. } => {
240 for subquery in subqueries {
241 self.bind_query_routines_for_storage(routines, subquery, params, outer)?;
242 }
243 }
244 }
245
246 let set_output = match &*root {
247 RelationalPlan::SetOp { .. } => Some(self.bind_root(routines, root, params, outer)?),
248 RelationalPlan::QueryBlock(_) | RelationalPlan::Values { .. } => None,
249 };
250 match root {
251 RelationalPlan::QueryBlock(block) => {
252 if block.from.is_none()
253 && block
254 .projections
255 .iter()
256 .any(|projection| matches!(projection.expr, ScalarExpr::Star))
257 && outer.is_none()
258 {
259 return Err(SQLError::Routine {
260 sqlstate: "42601".into(),
261 message: "SELECT * with no tables specified is not valid".into(),
262 });
263 }
264 let source_schema = block.from.as_ref().map_or_else(
265 || Ok(RowSchema::default()),
266 |source| self.bind_source(routines, source, &block.subqueries, params, outer),
267 )?;
268 let expression_schema = overlay_outer_schema(&source_schema, outer);
269 if let Some(filter) = block.r#where.as_mut() {
270 self.bind_scalar_routines_for_storage(
271 routines,
272 filter,
273 &expression_schema,
274 &block.subqueries,
275 params,
276 )?;
277 }
278 let labels = super::routine_parameters::column_labels(&block.projections);
279 for projection in &mut block.projections {
280 self.bind_scalar_routines_for_storage(
281 routines,
282 &mut projection.expr,
283 &expression_schema,
284 &block.subqueries,
285 params,
286 )?;
287 }
288 super::routine_parameters::keep_column_labels(&mut block.projections, labels);
289 if self.preserve_syntax_shape {
290 let mut expanded = (**block).clone();
292 expanded.projections = crate::semantics::expand_bound_projection_stars(
293 &block.projections,
294 &source_schema,
295 )?;
296 self.bind_grouping_variable_sites(&mut expanded, &source_schema);
297 crate::semantics::grouping_sets::bind_grouping_names(
298 routines,
299 &mut expanded,
300 &source_schema,
301 None,
302 params,
303 )?;
304 } else {
305 block.projections = crate::semantics::expand_bound_projection_stars(
306 &block.projections,
307 &source_schema,
308 )?;
309 self.bind_grouping_variable_sites(block, &source_schema);
310 crate::semantics::grouping_sets::bind_grouping_names(
311 routines,
312 block,
313 &source_schema,
314 None,
315 params,
316 )?;
317 }
318 let output_names = self.output_names(&block.projections);
319 for expression in block
320 .group_by
321 .iter_mut()
322 .chain(block.grouping_sets.iter_mut().flatten())
323 {
324 if self.preserve_syntax_shape
326 && matches!(expression, ScalarExpr::Column(name)
327 if !source_schema.has_unqualified_column(name)
328 && !source_schema.column_is_ambiguous(name)
329 && output_names.contains(name))
330 {
331 continue;
332 }
333 self.bind_scalar_routines_for_storage(
334 routines,
335 expression,
336 &expression_schema,
337 &block.subqueries,
338 params,
339 )?;
340 }
341 if let Some(having) = block.having.as_mut() {
342 self.bind_scalar_routines_for_storage(
343 routines,
344 having,
345 &expression_schema,
346 &block.subqueries,
347 params,
348 )?;
349 }
350 let names_output = |expression: &ScalarExpr| match expression {
352 ScalarExpr::Column(name) => output_names.contains(name),
353 _ => false,
354 };
355 for order in &mut block.order_by {
356 if names_output(&order.expr)
357 || self.output_takes_variable_site(&order.expr, &output_names)
358 {
359 continue;
360 }
361 self.bind_scalar_routines_for_storage(
362 routines,
363 &mut order.expr,
364 &expression_schema,
365 &block.subqueries,
366 params,
367 )?;
368 }
369 if let Some(limit) = block.limit.as_mut() {
370 self.bind_scalar_routines_for_storage(
371 routines,
372 limit,
373 &expression_schema,
374 &block.subqueries,
375 params,
376 )?;
377 }
378 if let Some(offset) = block.offset.as_mut() {
379 self.bind_scalar_routines_for_storage(
380 routines,
381 offset,
382 &expression_schema,
383 &block.subqueries,
384 params,
385 )?;
386 }
387 for expression in &mut block.distinct_on {
388 if names_output(expression)
389 || self.output_takes_variable_site(expression, &output_names)
390 {
391 continue;
392 }
393 self.bind_scalar_routines_for_storage(
394 routines,
395 expression,
396 &expression_schema,
397 &block.subqueries,
398 params,
399 )?;
400 }
401 for expression in block
402 .windows
403 .iter_mut()
404 .flat_map(|window| window.spec.expressions_mut())
405 {
406 self.bind_scalar_routines_for_storage(
407 routines,
408 expression,
409 &expression_schema,
410 &block.subqueries,
411 params,
412 )?;
413 }
414 }
415 RelationalPlan::SetOp {
416 order_by,
417 limit,
418 offset,
419 subqueries,
420 ..
421 } => {
422 let output = overlay_outer_schema(
423 set_output
424 .as_ref()
425 .expect("set-operation output schema was bound before routine expressions"),
426 outer,
427 );
428 let output = &output;
429 for order in order_by {
430 self.bind_scalar_routines_for_storage(
431 routines,
432 &mut order.expr,
433 output,
434 subqueries,
435 params,
436 )?;
437 }
438 if let Some(limit) = limit {
439 self.bind_scalar_routines_for_storage(
440 routines, limit, output, subqueries, params,
441 )?;
442 }
443 if let Some(offset) = offset {
444 self.bind_scalar_routines_for_storage(
445 routines, offset, output, subqueries, params,
446 )?;
447 }
448 }
449 RelationalPlan::Values { rows, subqueries } => {
450 let input = outer.cloned().unwrap_or_default();
451 for expression in rows.iter_mut().flatten() {
452 self.bind_scalar_routines_for_storage(
453 routines, expression, &input, subqueries, params,
454 )?;
455 }
456 }
457 }
458 self.bind_root(routines, root, params, outer)
459 }
460
461 #[expect(
462 clippy::too_many_lines,
463 reason = "preserves SELECT schema and row identity"
464 )]
465 pub(super) fn bind_source_routines_for_storage(
466 &mut self,
467 engine: &dyn RoutineResolution,
468 source: &mut SourcePlan,
469 subqueries: &[QueryPlan],
470 params: &[SQLParam],
471 outer: Option<&RowSchema>,
472 ) -> Result<(), SQLError> {
473 match source {
474 SourcePlan::Join {
475 left,
476 right,
477 on,
478 lateral,
479 ..
480 } => {
481 self.bind_source_routines_for_storage(engine, left, subqueries, params, outer)?;
482 let left_schema = self.bind_source(engine, left, subqueries, params, outer)?;
483 let implicit_lateral_function = matches!(
484 right.as_ref(),
485 SourcePlan::Function { .. } | SourcePlan::FunctionGroup { .. }
486 );
487 let right_scope = (*lateral || implicit_lateral_function)
488 .then(|| overlay_outer_schema(&left_schema, outer));
489 let right_outer = right_scope.as_ref().or(outer);
490 self.bind_source_routines_for_storage(
491 engine,
492 right,
493 subqueries,
494 params,
495 right_outer,
496 )?;
497 if let Some(on) = on {
498 let right_schema =
499 self.bind_source(engine, right, subqueries, params, right_outer)?;
500 let input = RowSchema::join(&left_schema, &right_schema, std::iter::empty());
501 let input = overlay_outer_schema(&input, outer);
502 self.bind_scalar_routines_for_storage(engine, on, &input, subqueries, params)?;
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,
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,
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,
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,
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,
578 )?;
579 }
580 }
581 Ok(())
582 }
583 SourcePlan::Table { .. } => Ok(()),
584 }
585 }
586
587 pub(super) 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 ) -> Result<(), SQLError> {
595 let schema = self.with_stored_outer_internal_aliases(schema);
596 let schema = &schema;
597 self.canonicalize_stored_outer_columns(expression, schema);
598 self.expand_stored_whole_rows(expression, schema);
599 self.canonicalize_routine_parameters(expression, schema);
600 self.resolve_variable_sites(expression, schema);
601 match self.scalar_binding {
602 super::ScalarBindingMode::References => return Ok(()),
603 super::ScalarBindingMode::CompositeInputs => {
604 return self.retain_composite_inputs_in_scope(
605 engine, expression, schema, subqueries, params,
606 )
607 }
608 super::ScalarBindingMode::Stored => {}
609 }
610 let mut failure = None;
611 crate::plan::rewrite_scalar_expression(expression, &mut |expression| {
612 if failure.is_some() {
613 return;
614 }
615 if matches!(expression, ScalarExpr::Func { order_syntax, name, binding, order_by, .. }
616 if super::ordered_calls::uses_ordered_arguments(*order_syntax, name, binding.as_ref(), order_by.len())
617 || super::ordered_calls::is_ordered_set(name))
618 {
619 if let Err(error) = self.bind_ordered_function_for_storage(
620 engine, expression, schema, subqueries, params,
621 ) {
622 failure = Some(error);
623 }
624 return;
625 }
626 let ScalarExpr::Func {
627 name,
628 binding,
629 args,
630 ..
631 } = expression
632 else {
633 return;
634 };
635 if let Some(dispatch) = binding.as_ref().and_then(|binding| binding.dispatch) {
636 if let crate::ast::FunctionDispatch::NumericOperator(operator) = dispatch {
637 let selected = (|| {
638 let resolver = self.query_function_type_resolver_for_subqueries(
639 engine, args, schema, subqueries, params,
640 )?;
641 let (_, types, _) = crate::function_call_argument_signature(
642 args,
643 schema,
644 params,
645 Some(&resolver),
646 )?;
647 crate::type_resolution::numeric_operator_types(operator, &types)
648 })();
649 match selected {
650 Ok(selected) => {
651 binding
652 .as_mut()
653 .expect("structural operator binding")
654 .argument_types = selected
655 .arguments
656 .iter()
657 .map(crate::ColumnType::sql_name)
658 .collect();
659 }
660 Err(error) => failure = Some(error),
661 }
662 }
663 return;
664 }
665 if let Err(error) = self.bind_scalar_function_for_storage(
666 engine, name, binding, args, schema, subqueries, params,
667 ) {
668 failure = Some(error);
669 return;
670 }
671 if !self.preserve_syntax_shape {
672 if let Err(error) =
673 crate::type_resolution::json_path::normalize_retained(expression)
674 {
675 failure = Some(error);
676 }
677 }
678 });
679 failure.map_or(Ok(()), Err)?;
680 let mut comparison_inputs = false;
681 expression.visit(&mut |node| {
682 comparison_inputs |= matches!(node, ScalarExpr::Func { binding: Some(binding), .. }
683 if matches!(binding.dispatch, Some(crate::ast::FunctionDispatch::IsDistinct | crate::ast::FunctionDispatch::ValueNullTest { .. })));
684 });
685 if comparison_inputs {
686 self.retain_composite_inputs_in_scope(engine, expression, schema, subqueries, params)?;
687 }
688 self.bind_stored_scalar_types(engine, expression, schema, subqueries, params)?;
689 self.record_composite_dependencies(engine, expression, schema, subqueries, params)
690 }
691
692 fn bind_stored_scalar_types(
694 &mut self,
695 engine: &dyn RoutineResolution,
696 expression: &mut ScalarExpr,
697 schema: &RowSchema,
698 subqueries: &[QueryPlan],
699 params: &[SQLParam],
700 ) -> Result<(), SQLError> {
701 super::stored_types::bind_scalar_type_identities(expression, &mut |name| {
702 engine.resolve_type_name(name)
703 })?;
704 let resolver = self.query_function_type_resolver_for_subqueries(
705 engine,
706 std::slice::from_ref(expression),
707 schema,
708 subqueries,
709 params,
710 )?;
711 if self.preserve_syntax_shape {
713 crate::type_resolution::store_syntax_operand_coercions(
714 expression, schema, params, &resolver,
715 )?;
716 } else {
717 crate::type_resolution::store_operand_coercions(expression, schema, params, &resolver)?;
718 }
719 if !crate::type_resolution::contains_unknown_literal(expression) {
720 return Ok(());
721 }
722 crate::type_resolution::fold_stored_enum_constants(expression, schema, params, &resolver)
723 .map(drop)
724 }
725
726 #[expect(
727 clippy::too_many_arguments,
728 reason = "keeps execution context inputs aligned"
729 )]
730 fn bind_scalar_function_for_storage(
731 &mut self,
732 engine: &dyn RoutineResolution,
733 name: &str,
734 binding: &mut Option<FunctionBinding>,
735 args: &[ScalarExpr],
736 schema: &RowSchema,
737 subqueries: &[QueryPlan],
738 params: &[SQLParam],
739 ) -> Result<(), SQLError> {
740 let resolver = self.query_function_type_resolver_for_subqueries(
741 engine, args, schema, subqueries, params,
742 )?;
743 let (argument_names, argument_types, explicit_variadic) =
744 crate::function_call_argument_signature(args, schema, params, Some(&resolver))?;
745 let selected = if crate::is_fixed_builtin(name) {
746 crate::resolve_fixed_builtin_call(
747 name,
748 binding.as_ref(),
749 &argument_names,
750 &argument_types,
751 explicit_variadic,
752 Some(&resolver),
753 )?
754 .map(|resolved| resolved.selected)
755 } else if crate::catalog::json_path::JsonPathFunction::lookup(name).is_some() {
756 crate::type_resolution::json_path::resolve_call(
757 name,
758 binding.as_ref(),
759 crate::type_resolution::RoutineCallDescriptor {
760 argument_names: &argument_names,
761 argument_types: &argument_types,
762 explicit_variadic,
763 },
764 Some(&resolver),
765 &uqa_core::memory::ProductionControl::uncontrolled(),
766 )?
767 .map(|selected| {
768 selected
769 .into_uncontrolled()
770 .expect("ordinary stored binding")
771 })
772 } else if let Some(array) = crate::type_resolution::resolve_array_transform_call(
773 name,
774 binding.as_ref(),
775 args,
776 &argument_types,
777 params,
778 explicit_variadic,
779 &resolver,
780 )? {
781 array.overload
782 } else {
783 resolver.resolve_function_overload(
784 name,
785 binding.as_ref(),
786 &argument_names,
787 &argument_types,
788 explicit_variadic,
789 )?
790 };
791 if let Some(selected) = selected {
792 *binding = Some(selected.binding);
793 }
794 Ok(())
795 }
796}
797
798pub fn bind_query_plan_routines_for_storage(
799 engine: &dyn RoutineResolution,
800 plan: &mut QueryPlan,
801 params: &[SQLParam],
802 ctes: &BindingContext,
803 outer: Option<&RowSchema>,
804) -> Result<RowSchema, SQLError> {
805 SchemaScope::for_analysis(ctes)?.bind_query_routines_for_storage(engine, plan, params, outer)
806}
807
808pub fn bind_syntax_query_plan_routines(
810 engine: &dyn RoutineResolution,
811 plan: &mut QueryPlan,
812 params: &[SQLParam],
813 ctes: &BindingContext,
814 outer: Option<&RowSchema>,
815) -> Result<RowSchema, SQLError> {
816 let mut scope = SchemaScope::for_analysis(ctes)?;
817 scope.preserve_syntax_shape = true;
818 scope.bind_query_routines_for_storage(engine, plan, params, outer)
819}
820
821pub fn bind_expression_plan_routines_for_storage(
823 engine: &dyn RoutineResolution,
824 plan: &mut ExpressionPlan,
825 params: &[SQLParam],
826 ctes: &BindingContext,
827 schema: &RowSchema,
828) -> Result<Option<ColumnType>, SQLError> {
829 bind_stored_expression(engine, plan, params, ctes, schema, None)
830}
831
832pub(super) fn bind_rule_condition_routines(
833 engine: &dyn RoutineResolution,
834 plan: &mut ExpressionPlan,
835 ctes: &BindingContext,
836 schema: &RowSchema,
837 event: crate::ast::RuleEvent,
838) -> Result<Option<ColumnType>, SQLError> {
839 bind_stored_expression(engine, plan, &[], ctes, schema, Some(event))
840}
841
842fn bind_stored_expression(
843 engine: &dyn RoutineResolution,
844 plan: &mut ExpressionPlan,
845 params: &[SQLParam],
846 ctes: &BindingContext,
847 schema: &RowSchema,
848 rule_event: Option<crate::ast::RuleEvent>,
849) -> Result<Option<ColumnType>, SQLError> {
850 let mut scope = SchemaScope::for_analysis(ctes)?;
851 scope.rule_condition_event = rule_event;
852 scope.preserve_syntax_shape = true;
853 scope.stored_expression_outer = Some(schema.clone());
854 for subquery in &mut plan.subqueries {
855 scope.bind_query_routines_for_storage(engine, subquery, params, Some(schema))?;
856 }
857 scope.bind_scalar_routines_for_storage(
858 engine,
859 &mut plan.scalar,
860 schema,
861 &plan.subqueries,
862 params,
863 )?;
864 scope.bind_expression_type(engine, &plan.scalar, schema, &plan.subqueries, params)
865}