1mod analysis;
15pub mod catalog_sources;
16mod commands;
17mod cte_controls;
18mod ctes;
19mod merge_scopes;
20mod preparation;
21mod projection;
22mod routine_binding;
23mod scope;
24mod sources;
25pub mod statements;
26mod type_resolution;
27
28pub use commands::analyze_prepared_command_schema;
29pub use preparation::infer_prepared_parameter_types;
30
31#[cfg(test)]
32mod tests;
33
34pub use cte_controls::{analyze_recursive_control_step, extend_cte_generated_schema};
35pub use projection::{
36 analyze_projection_output_schema, bind_projection_output_schema,
37 validate_query_block_expression_types, validate_query_block_references,
38};
39pub use routine_binding::{
40 bind_expression_plan_routines_for_storage, bind_query_plan_routines_for_storage,
41};
42pub use scope::{
43 analyze_expression_plan_type, analyze_query_plan_schema,
44 analyze_query_plan_schema_with_catalog, bind_expression_plan_type, bind_query_plan_schema,
45};
46pub use scope::{overlay_outer_schema, values_types_in_scope};
47pub use sources::{
48 analyze_source_plan_schema, bind_source_plan_schema, bind_source_plan_schema_for_execution,
49 with_query_table_pseudo_columns,
50};
51
52use catalog_sources::operator_join_relation_schemas;
53use cte_controls::extend_recursive_cte_binding_schema;
54pub use cte_controls::hide_recursive_generated_schema;
55use projection::{projection_star_columns, rename_schema};
56use scope::merge_types;
57use sources::{alias_table_schema, table_function_member_source, JoinSchemaBinding};
58use type_resolution::{set_operation_output_schema, QueryFunctionTypeResolver};
59
60use crate::plan::{QueryBlockPlan, QueryPlan, RelationalPlan, SourcePlan};
61use crate::semantics::{cte_references_own_name, expr_contains_subquery, projection_columns};
62use crate::{SQLError, SQLParam, ScalarExpr};
63use catalog_sources::user_function_output_columns;
64pub use context::BindingContext;
65use uqa_core::Value;
66
67pub mod context;
68use crate::ast::ColumnType;
69use crate::catalog::analysis::CatalogReadView;
70use crate::catalog::resolution::RelationNameResolution;
71use crate::routines::RoutineResolution;
72use crate::semantics::{
73 alias_join_schema, apply_table_function_aliases, join_using_output_schema, resolve_join_using,
74 table_function_column_types, table_function_empty_schema, validate_table_function_alias_count,
75 validate_table_function_column_definition, TableFunctionTypeRequest,
76};
77use crate::RowSchema;
78use std::collections::{BTreeMap, BTreeSet};
79
80struct SchemaScope {
81 catalog: CatalogReadView,
82 resolution: RelationNameResolution,
83 ctes: BTreeMap<String, RowSchema>,
84 deferred_ctes: BTreeMap<String, crate::plan::CtePlan>,
85 non_returning_ctes: BTreeSet<String>,
86 visiting_views: BTreeSet<String>,
87 validate_references: bool,
88 stored_expression_outer: Option<RowSchema>,
89}
90
91fn non_returning_cte_error(name: &str) -> SQLError {
92 SQLError::Unsupported(format!(
93 "WITH query \"{name}\" does not have a RETURNING clause"
94 ))
95}
96
97impl SchemaScope {
98 fn from_context(ctes: &BindingContext) -> Result<Self, SQLError> {
99 Ok(Self {
100 catalog: ctes.catalog.clone(),
101 resolution: ctes.resolution.clone(),
102 ctes: ctes.ctes.clone(),
103 deferred_ctes: ctes.deferred_ctes.clone(),
104 non_returning_ctes: ctes.non_returning_ctes.clone(),
105 visiting_views: BTreeSet::new(),
106 validate_references: false,
107 stored_expression_outer: None,
108 })
109 }
110
111 fn for_analysis(ctes: &BindingContext) -> Result<Self, SQLError> {
112 let mut scope = Self::from_context(ctes)?;
113 scope.validate_references = true;
114 Ok(scope)
115 }
116
117 fn for_catalog_analysis(catalog: CatalogReadView, resolution: RelationNameResolution) -> Self {
118 Self {
119 catalog,
120 resolution,
121 ctes: BTreeMap::new(),
122 deferred_ctes: BTreeMap::new(),
123 non_returning_ctes: BTreeSet::new(),
124 visiting_views: BTreeSet::new(),
125 validate_references: true,
126 stored_expression_outer: None,
127 }
128 }
129
130 fn bind_query(
131 &mut self,
132 routines: &dyn RoutineResolution,
133 plan: &QueryPlan,
134 params: &[SQLParam],
135 outer: Option<&RowSchema>,
136 ) -> Result<RowSchema, SQLError> {
137 self.bind_query_mode(routines, plan, params, outer, false)
138 }
139
140 fn bind_set_operand(
141 &mut self,
142 routines: &dyn RoutineResolution,
143 plan: &QueryPlan,
144 params: &[SQLParam],
145 outer: Option<&RowSchema>,
146 ) -> Result<RowSchema, SQLError> {
147 self.bind_query_mode(routines, plan, params, outer, true)
148 }
149
150 fn bind_query_mode(
151 &mut self,
152 routines: &dyn RoutineResolution,
153 plan: &QueryPlan,
154 params: &[SQLParam],
155 outer: Option<&RowSchema>,
156 preserve_top_level_unknown: bool,
157 ) -> Result<RowSchema, SQLError> {
158 let lookup_mode = if plan.relations_bound {
159 crate::catalog::resolution::RelationLookupMode::Bound
160 } else {
161 crate::catalog::resolution::RelationLookupMode::Dynamic
162 };
163 let previous = self.resolution.set_lookup_mode(lookup_mode);
164 let result =
165 self.bind_query_mode_inner(routines, plan, params, outer, preserve_top_level_unknown);
166 self.resolution.set_lookup_mode(previous);
167 result
168 }
169
170 fn bind_query_mode_inner(
171 &mut self,
172 routines: &dyn RoutineResolution,
173 plan: &QueryPlan,
174 params: &[SQLParam],
175 outer: Option<&RowSchema>,
176 preserve_top_level_unknown: bool,
177 ) -> Result<RowSchema, SQLError> {
178 let previous = self.bind_cte_schemas(routines, &plan.ctes, params, outer)?;
179
180 let result = self.bind_root(
181 routines,
182 &plan.root,
183 params,
184 outer,
185 preserve_top_level_unknown,
186 );
187 self.restore_cte_schemas(previous);
188 result
189 }
190
191 fn bind_recursive_seed(
192 &mut self,
193 routines: &dyn RoutineResolution,
194 plan: &QueryPlan,
195 params: &[SQLParam],
196 outer: Option<&RowSchema>,
197 ) -> Result<RowSchema, SQLError> {
198 match &plan.root {
199 RelationalPlan::SetOp { left, .. } => self.bind_query(routines, left, params, outer),
200 _ => self.bind_root(routines, &plan.root, params, outer, false),
201 }
202 }
203
204 fn bind_root(
205 &mut self,
206 routines: &dyn RoutineResolution,
207 root: &RelationalPlan,
208 params: &[SQLParam],
209 outer: Option<&RowSchema>,
210 preserve_top_level_unknown: bool,
211 ) -> Result<RowSchema, SQLError> {
212 match root {
213 RelationalPlan::QueryBlock(block) => {
214 self.bind_query_block(routines, block, params, outer, preserve_top_level_unknown)
215 }
216 RelationalPlan::SetOp {
217 kind,
218 all,
219 left,
220 right,
221 order_by,
222 limit,
223 offset,
224 subqueries,
225 ..
226 } => {
227 let left = self.bind_set_operand(routines, left, params, outer)?;
228 let right = self.bind_set_operand(routines, right, params, outer)?;
229 if left.len() != right.len() {
230 return Err(SQLError::TypeMismatch(format!(
231 "set operation has {} columns on the left and {} on the right",
232 left.len(),
233 right.len()
234 )));
235 }
236 let output = set_operation_output_schema(&left, &right, *kind, *all)?;
237 if self.validate_references {
238 self.validate_set_operation_clauses(
239 routines,
240 analysis::SetOperationClauses {
241 order_by,
242 limit: limit.as_deref(),
243 offset: offset.as_deref(),
244 subqueries,
245 output: &output,
246 },
247 params,
248 )?;
249 }
250 Ok(output)
251 }
252 RelationalPlan::Values { rows, subqueries } => {
253 let columns = rows.first().map_or_else(Vec::new, |row| {
254 (1..=row.len())
255 .map(|index| format!("column{index}"))
256 .collect()
257 });
258 let types =
259 self.bind_values_types(routines, rows, subqueries, outer, params, outer)?;
260 Ok(RowSchema::with_types(columns, types))
261 }
262 }
263 }
264
265 fn bind_query_block(
266 &mut self,
267 routines: &dyn RoutineResolution,
268 block: &QueryBlockPlan,
269 params: &[SQLParam],
270 outer: Option<&RowSchema>,
271 preserve_top_level_unknown: bool,
272 ) -> Result<RowSchema, SQLError> {
273 let source = block.from.as_ref().map_or_else(
274 || Ok(RowSchema::default()),
275 |source| self.bind_source(routines, source, &block.subqueries, params, outer),
276 )?;
277 let source = if self.validate_references {
278 analysis::with_query_source_columns(&source, block)
279 } else {
280 source
281 };
282 let expression_schema = overlay_outer_schema(&source, outer);
283 let labels = projection_columns(&block.projections);
284 let mut columns = Vec::new();
285 let mut types = Vec::new();
286 for (position, projection) in block.projections.iter().enumerate() {
287 if let Some(star_columns) = projection_star_columns(&projection.expr, &source)? {
288 for (column, ty) in star_columns {
289 columns.push(column);
290 types.push(ty);
291 }
292 continue;
293 }
294 columns.push(labels[position].clone());
295 types.push(
296 if preserve_top_level_unknown
297 && matches!(
298 &projection.expr,
299 ScalarExpr::Literal(Value::Str(_) | Value::Null)
300 )
301 {
302 None
303 } else if matches!(&projection.expr, ScalarExpr::Literal(Value::Null)) {
304 Some(ColumnType::Text)
305 } else {
306 self.bind_expression_type(
307 routines,
308 &projection.expr,
309 &expression_schema,
310 &block.subqueries,
311 params,
312 outer,
313 )?
314 },
315 );
316 }
317 let output = RowSchema::with_types(columns, types);
318 let output = analysis::with_projected_open_columns(&output, &block.projections, &source);
319 if self.validate_references {
320 self.validate_query_block_clauses(
321 routines,
322 block,
323 &expression_schema,
324 &output,
325 params,
326 )?;
327 }
328 Ok(output)
329 }
330
331 fn bind_expression_type(
332 &mut self,
333 routines: &dyn RoutineResolution,
334 expression: &ScalarExpr,
335 schema: &RowSchema,
336 subqueries: &[QueryPlan],
337 params: &[SQLParam],
338 outer: Option<&RowSchema>,
339 ) -> Result<Option<ColumnType>, SQLError> {
340 if let ScalarExpr::ScalarSubquery(index) = expression {
341 let plan = subqueries.get(*index).ok_or_else(|| {
342 SQLError::Internal(format!("scalar subquery slot {index} is out of bounds"))
343 })?;
344 let subquery_outer = self.validate_references.then_some(schema).or(outer);
345 let output = self.bind_query(routines, plan, params, subquery_outer)?;
346 return Ok(output.column_type(0).cloned());
347 }
348 let schema = self.with_stored_outer_internal_aliases(schema);
349 let schema = &schema;
350 let resolver = self.query_function_type_resolver(
351 routines, expression, schema, subqueries, params, outer,
352 )?;
353 if self.validate_references {
354 Self::validate_expression_references_with_resolver(
355 routines, expression, schema, None, params, &resolver,
356 )?;
357 }
358 crate::scalar_type_with_resolver(expression, schema, params, &resolver)
359 }
360
361 fn bind_values_types(
362 &mut self,
363 routines: &dyn RoutineResolution,
364 rows: &[Vec<ScalarExpr>],
365 subqueries: &[QueryPlan],
366 schema: Option<&RowSchema>,
367 params: &[SQLParam],
368 outer: Option<&RowSchema>,
369 ) -> Result<Vec<Option<ColumnType>>, SQLError> {
370 let width = rows.first().map_or(0, Vec::len);
371 let empty = RowSchema::default();
372 let schema = schema.unwrap_or(&empty);
373 let mut types = vec![None; width];
374 for row in rows {
375 if row.len() != width {
376 return Err(SQLError::TypeMismatch(
377 "VALUES lists must all be the same length".into(),
378 ));
379 }
380 for (position, expression) in row.iter().enumerate() {
381 let candidate =
382 if matches!(expression, ScalarExpr::Literal(Value::Str(_) | Value::Null)) {
383 None
384 } else {
385 self.bind_expression_type(
386 routines, expression, schema, subqueries, params, outer,
387 )?
388 };
389 types[position] = merge_types(types[position].as_ref(), candidate.as_ref())?;
390 }
391 }
392 Ok(types
393 .into_iter()
394 .map(|ty| ty.or(Some(ColumnType::Text)))
395 .collect())
396 }
397
398 fn bind_join_output_schema(
399 &mut self,
400 binding: JoinSchemaBinding<'_>,
401 ) -> Result<RowSchema, SQLError> {
402 let JoinSchemaBinding {
403 routines,
404 kind,
405 on,
406 using,
407 natural,
408 alias,
409 column_aliases,
410 left,
411 right,
412 subqueries,
413 params,
414 outer,
415 } = binding;
416 if let Some(on) = on {
417 let input = RowSchema::join(left, right, std::iter::empty::<String>());
418 let input = overlay_outer_schema(&input, outer);
419 if self.validate_references {
420 self.bind_expression_type(routines, on, &input, subqueries, params, outer)?;
421 } else {
422 crate::scalar_type_with_resolver(on, &input, params, routines)?;
423 }
424 }
425 let resolved = resolve_join_using(using, natural, left, right)?;
426 let schema = resolved.map_or_else(
427 || Ok(RowSchema::join(left, right, std::iter::empty())),
428 |using| join_using_output_schema(kind, left, right, &using),
429 )?;
430 alias_join_schema(&schema, alias, column_aliases)
431 }
432
433 #[expect(
434 clippy::too_many_lines,
435 reason = "preserves SELECT schema and row identity"
436 )]
437 fn bind_source_inner(
438 &mut self,
439 routines: &dyn RoutineResolution,
440 source: &SourcePlan,
441 subqueries: &[QueryPlan],
442 params: &[SQLParam],
443 outer: Option<&RowSchema>,
444 ) -> Result<RowSchema, SQLError> {
445 match source {
446 SourcePlan::Table {
447 name,
448 qualifier,
449 alias,
450 column_aliases,
451 bound_columns,
452 ..
453 } => {
454 let qualifier = alias.as_deref().unwrap_or(qualifier);
455 let cte_name = crate::semantics::cte_reference_name(name);
456 if let Some(schema) = cte_name.as_ref().and_then(|name| self.ctes.get(name)) {
457 if let Some(name) = cte_name
458 .as_ref()
459 .filter(|name| self.non_returning_ctes.contains(*name))
460 {
461 return Err(non_returning_cte_error(name));
462 }
463 return alias_table_schema(schema, qualifier, column_aliases);
464 }
465 if let Some(plan) = cte_name
466 .as_ref()
467 .and_then(|name| self.deferred_ctes.get(name))
468 {
469 if !plan.body.returns_rows() {
470 return Err(non_returning_cte_error(&plan.name));
471 }
472 }
473 if let Some(plan) = cte_name.and_then(|name| self.deferred_ctes.remove(&name)) {
474 let result = self
475 .bind_cte_body(routines, &plan.body, params, outer)
476 .and_then(|schema| {
477 let schema = rename_schema(&schema, &plan.columns, Some(qualifier));
478 alias_table_schema(&schema, qualifier, column_aliases)
479 });
480 self.deferred_ctes.insert(plan.name.clone(), plan);
481 return result;
482 }
483 if self.catalog.sequence_exists(&self.resolution, name)? {
484 let schema = RowSchema::with_qualified_types(
485 qualifier,
486 vec!["last_value".into(), "log_cnt".into(), "is_called".into()],
487 vec![
488 Some(ColumnType::BigInteger),
489 Some(ColumnType::BigInteger),
490 Some(ColumnType::Boolean),
491 ],
492 );
493 return alias_table_schema(&schema, qualifier, column_aliases);
494 }
495 let view = self.catalog.view_resolved(&self.resolution, name)?;
496 if let Some(view) = view {
497 if view.materialized {
498 let columns = view.output_columns.unwrap_or_default();
499 if columns.len() != view.materialized_column_types.len() {
500 return Err(SQLError::Internal(format!(
501 "materialized view `{name}` has {} columns but {} stored column types",
502 columns.len(),
503 view.materialized_column_types.len()
504 )));
505 }
506 let schema = RowSchema::with_qualified_types(
507 qualifier,
508 columns,
509 view.materialized_column_types,
510 );
511 return alias_table_schema(&schema, qualifier, column_aliases);
512 }
513 let key = name.to_ascii_lowercase();
514 if !self.visiting_views.insert(key.clone()) {
515 return Err(SQLError::Internal(format!(
516 "view `{name}` has a recursive schema dependency"
517 )));
518 }
519 let result = self
520 .bind_query(routines, &view.query, params, outer)
521 .and_then(|schema| {
522 let schema = rename_schema(
523 &schema,
524 view.output_columns.as_deref().unwrap_or(&[]),
525 Some(qualifier),
526 );
527 alias_table_schema(&schema, qualifier, column_aliases)
528 });
529 self.visiting_views.remove(&key);
530 return result;
531 }
532 let table = self.catalog.table_resolved(&self.resolution, name)?;
533 if let Some(table) = table {
534 let columns = table
535 .columns
536 .iter()
537 .map(|column| column.name.clone())
538 .collect();
539 let types = table
540 .columns
541 .iter()
542 .map(|column| Some(column.ty.clone()))
543 .collect();
544 let schema = RowSchema::with_qualified_types(qualifier, columns, types);
545 let schema =
546 crate::semantics::bound_source_schema(&schema, bound_columns.as_deref())?;
547 let schema = alias_table_schema(&schema, qualifier, column_aliases)?;
548 let schema = if table.columns.is_empty() && !table.columns_declared {
549 RowSchema::with_open_columns(&schema, Some(qualifier))
550 } else {
551 schema
552 };
553 return Ok(analysis::with_table_pseudo_columns(&schema, qualifier));
554 }
555 let foreign_table = self
556 .catalog
557 .foreign_table_resolved(&self.resolution, name)?;
558 if let Some(foreign_table) = foreign_table {
559 let typed_columns = foreign_table
560 .columns
561 .iter()
562 .map(|column| (column.name.clone(), column.ty.clone()))
563 .collect::<Vec<_>>();
564 let columns = typed_columns
565 .iter()
566 .map(|(column, _)| column.clone())
567 .collect();
568 let types = typed_columns.into_iter().map(|(_, ty)| Some(ty)).collect();
569 let schema = RowSchema::with_qualified_types(qualifier, columns, types);
570 let schema =
571 crate::semantics::bound_source_schema(&schema, bound_columns.as_deref())?;
572 return alias_table_schema(&schema, qualifier, column_aliases);
573 }
574 if let Some(schema) = self
575 .catalog
576 .virtual_relation_schema(&self.resolution, name)?
577 {
578 let (columns, types): (Vec<_>, Vec<_>) = schema
579 .into_iter()
580 .map(|(column, ty)| (column, Some(ty)))
581 .unzip();
582 let schema = RowSchema::with_qualified_types(qualifier, columns, types);
583 return alias_table_schema(&schema, qualifier, column_aliases);
584 }
585 Err(SQLError::UnknownTable(name.clone()))
586 }
587 SourcePlan::Values {
588 rows,
589 alias,
590 column_aliases,
591 internal_relation,
592 internal_column_types,
593 } => {
594 if let Some(relation) = internal_relation {
595 return Ok(RowSchema::with_internal_relation_types(
596 *relation,
597 internal_column_types.clone(),
598 ));
599 }
600 let columns = if column_aliases.is_empty() {
601 (1..=rows.first().map_or(0, Vec::len))
602 .map(|index| format!("column{index}"))
603 .collect::<Vec<_>>()
604 } else {
605 column_aliases.clone()
606 };
607 let binding_schema = outer.cloned().unwrap_or_default();
608 let types = self.bind_values_types(
609 routines,
610 rows,
611 subqueries,
612 Some(&binding_schema),
613 params,
614 Some(&binding_schema),
615 )?;
616 Ok(match alias.as_deref() {
617 Some(qualifier) => RowSchema::with_qualified_types(qualifier, columns, types),
618 None => RowSchema::with_types(columns, types),
619 })
620 }
621 SourcePlan::Function {
622 name,
623 binding,
624 output_name,
625 relations,
626 args,
627 alias,
628 column_aliases,
629 ordinality,
630 column_types,
631 ..
632 } => {
633 let lower = crate::semantics::builtin_function_dispatch_name(name);
634 let operator_join = crate::registry::is_operator_join_table_function(&lower);
635 let operator_inputs = operator_join
636 .then(|| {
637 operator_join_relation_schemas(
638 &self.catalog,
639 &self.resolution,
640 relations.as_ref(),
641 )
642 })
643 .transpose()?;
644 let input = outer.cloned().unwrap_or_default();
645 let type_resolver = self.query_function_type_resolver_for_subqueries(
646 routines,
647 args,
648 &input,
649 subqueries,
650 params,
651 Some(&input),
652 )?;
653 let user_function = if let Some((left, right)) = operator_inputs.as_ref() {
654 if self.validate_references {
655 let constant = RowSchema::default();
656 for (position, argument) in args.iter().enumerate() {
657 let schema = match position {
658 0 => left,
659 1 => right,
660 _ => &constant,
661 };
662 self.validate_expression_references(
663 routines, argument, schema, None, subqueries, params,
664 )?;
665 }
666 }
667 None
668 } else if self.validate_references {
669 self.validate_table_function_source(
670 routines,
671 analysis::TableFunctionSourceValidation {
672 name,
673 binding: binding.as_ref(),
674 args,
675 subqueries,
676 input: &input,
677 params,
678 },
679 )?
680 } else {
681 crate::semantics::resolve_user_table_function(
682 routines,
683 name,
684 binding.as_ref(),
685 args,
686 &input,
687 params,
688 &type_resolver,
689 )?
690 };
691 validate_table_function_column_definition(
692 name,
693 binding.as_ref(),
694 user_function
695 .as_ref()
696 .map(|resolved| resolved.function.as_ref()),
697 column_types,
698 )?;
699 let catalog_columns = if !self.validate_references && user_function.is_none() {
700 user_function_output_columns(self.catalog.as_ref(), &self.resolution, name)?
701 } else {
702 None
703 };
704 let columns = user_function
705 .as_ref()
706 .and_then(|resolved| {
707 crate::semantics::user_function_output_columns_for(&resolved.function)
708 })
709 .or(catalog_columns)
710 .map_or_else(
711 || {
712 table_function_empty_schema(
713 name,
714 output_name,
715 alias.as_deref(),
716 column_aliases,
717 args.len(),
718 *ordinality,
719 )
720 },
721 |columns| {
722 apply_table_function_aliases(columns, column_aliases, *ordinality)
723 },
724 );
725 validate_table_function_alias_count(
726 alias.as_deref().unwrap_or(output_name),
727 columns.len(),
728 column_aliases.len(),
729 )?;
730 let types = table_function_column_types(
731 routines,
732 TableFunctionTypeRequest {
733 name,
734 args,
735 user_function: user_function
736 .as_ref()
737 .map(|resolved| resolved.function.as_ref()),
738 user_invocation: user_function
739 .as_ref()
740 .and_then(|resolved| resolved.binding.invocation.as_deref()),
741 declared_types: column_types,
742 columns: &columns,
743 ordinality: *ordinality,
744 },
745 &input,
746 params,
747 &type_resolver,
748 );
749 let qualifier = alias.as_deref().unwrap_or(output_name);
750 let schema = RowSchema::with_qualified_types(qualifier, columns, types);
751 Ok(
752 if user_function.is_none()
753 && column_types.is_empty()
754 && routines.has_registered_table_function(name)
755 {
756 RowSchema::with_open_columns(&schema, Some(qualifier))
757 } else {
758 schema
759 },
760 )
761 }
762 SourcePlan::FunctionGroup {
763 functions,
764 alias,
765 column_aliases,
766 ordinality,
767 } => {
768 let first = functions
769 .first()
770 .ok_or_else(|| SQLError::Internal("ROWS FROM group has no functions".into()))?;
771 let mut columns = Vec::new();
772 let mut types = Vec::new();
773 let mut open = false;
774 for function in functions {
775 let member = table_function_member_source(function);
776 let schema = self.bind_source(routines, &member, subqueries, params, outer)?;
777 open |= schema.columns_are_open(None);
778 columns.extend(schema.iter().enumerate().map(|(position, column)| {
779 schema.public_name(position).unwrap_or(column).to_string()
780 }));
781 types.extend(schema.column_types().iter().cloned());
782 }
783 if *ordinality {
784 columns.push("ordinality".into());
785 types.push(Some(ColumnType::BigInteger));
786 }
787 let qualifier = alias.as_deref().unwrap_or(&first.output_name);
788 validate_table_function_alias_count(
789 qualifier,
790 columns.len(),
791 column_aliases.len(),
792 )?;
793 for (column, alias) in columns.iter_mut().zip(column_aliases) {
794 column.clone_from(alias);
795 }
796 let schema = RowSchema::with_qualified_types(qualifier, columns, types);
797 Ok(if open {
798 RowSchema::with_open_columns(&schema, Some(qualifier))
799 } else {
800 schema
801 })
802 }
803 SourcePlan::Subquery {
804 body,
805 alias,
806 column_aliases,
807 } => {
808 let schema = self.bind_query(routines, body, params, outer)?;
809 Ok(rename_schema(&schema, column_aliases, alias.as_deref()))
810 }
811 SourcePlan::Join {
812 left,
813 right,
814 kind,
815 on,
816 using,
817 natural,
818 alias,
819 column_aliases,
820 lateral,
821 ..
822 } => {
823 let left_schema = self.bind_source(routines, left, subqueries, params, outer)?;
824 let implicit_lateral_function = matches!(
825 right.as_ref(),
826 SourcePlan::Function { .. } | SourcePlan::FunctionGroup { .. }
827 );
828 let right_scope = (*lateral || implicit_lateral_function)
829 .then(|| overlay_outer_schema(&left_schema, outer));
830 let right_schema = self.bind_source(
831 routines,
832 right,
833 subqueries,
834 params,
835 right_scope.as_ref().or(outer),
836 )?;
837 self.bind_join_output_schema(JoinSchemaBinding {
838 routines,
839 kind: *kind,
840 on: on.as_ref(),
841 using: using.as_ref(),
842 natural: *natural,
843 alias: alias.as_deref(),
844 column_aliases,
845 left: &left_schema,
846 right: &right_schema,
847 subqueries,
848 params,
849 outer,
850 })
851 }
852 }
853 }
854
855 fn bind_source_for_execution(
856 &mut self,
857 routines: &dyn RoutineResolution,
858 source: &mut SourcePlan,
859 subqueries: &[QueryPlan],
860 params: &[SQLParam],
861 outer: Option<&RowSchema>,
862 ) -> Result<RowSchema, SQLError> {
863 match source {
864 SourcePlan::Join {
865 left,
866 right,
867 kind,
868 on,
869 using,
870 natural,
871 alias,
872 column_aliases,
873 lateral,
874 ..
875 } => {
876 let left_schema =
877 self.bind_source_for_execution(routines, left, subqueries, params, outer)?;
878 let implicit_lateral_function = matches!(
879 right.as_ref(),
880 SourcePlan::Function { .. } | SourcePlan::FunctionGroup { .. }
881 );
882 let right_scope = (*lateral || implicit_lateral_function)
883 .then(|| overlay_outer_schema(&left_schema, outer));
884 let right_schema = self.bind_source_for_execution(
885 routines,
886 right,
887 subqueries,
888 params,
889 right_scope.as_ref().or(outer),
890 )?;
891 return self.bind_join_output_schema(JoinSchemaBinding {
892 routines,
893 kind: *kind,
894 on: on.as_ref(),
895 using: using.as_ref(),
896 natural: *natural,
897 alias: alias.as_deref(),
898 column_aliases,
899 left: &left_schema,
900 right: &right_schema,
901 subqueries,
902 params,
903 outer,
904 });
905 }
906 SourcePlan::Function {
907 name,
908 binding,
909 relations,
910 args,
911 ..
912 } => {
913 let lower = crate::semantics::builtin_function_dispatch_name(name);
914 if crate::registry::is_operator_join_table_function(&lower) {
915 operator_join_relation_schemas(
916 &self.catalog,
917 &self.resolution,
918 relations.as_ref(),
919 )?;
920 return self.bind_source(routines, source, subqueries, params, outer);
921 }
922 let input = outer.cloned().unwrap_or_default();
923 let resolver = self.query_function_type_resolver_for_subqueries(
924 routines,
925 args,
926 &input,
927 subqueries,
928 params,
929 Some(&input),
930 )?;
931 if let Some(selected) = crate::semantics::resolve_table_function_binding(
932 routines,
933 name,
934 binding.as_ref(),
935 args,
936 &input,
937 params,
938 &resolver,
939 )? {
940 *binding = Some(selected);
941 }
942 }
943 SourcePlan::FunctionGroup { functions, .. } => {
944 for function in functions {
945 let mut member = table_function_member_source(function);
946 self.bind_source_for_execution(
947 routines,
948 &mut member,
949 subqueries,
950 params,
951 outer,
952 )?;
953 let SourcePlan::Function { binding, .. } = member else {
954 unreachable!("table-function member changed source kind during binding")
955 };
956 function.binding = binding;
957 }
958 }
959 SourcePlan::Table { .. } | SourcePlan::Values { .. } | SourcePlan::Subquery { .. } => {}
960 }
961 self.bind_source(routines, source, subqueries, params, outer)
962 }
963}
964
965#[cfg(test)]
966mod fixture;
967
968pub mod correlation;
969
970pub mod stored_columns;
971pub mod stored_relations;
972pub mod stored_routines;
973
974pub mod scoped_types;
975pub mod snapshot;
976
977pub mod view_dependencies;
978
979pub mod portals;