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