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