1use crate::ast::{JoinKind, JoinUsing};
10use crate::plan::{QueryPlan, SourcePlan, TableFunctionPlan};
11use crate::RowSchema;
12use crate::{SQLError, SQLParam};
13
14use super::{
15 analysis, operator_join_relation_schemas, overlay_outer_schema, projection::rename_schema,
16 BindingContext, ScalarExpr, SchemaScope,
17};
18use crate::routines::RoutineResolution;
19
20pub(super) fn alias_table_schema(
21 schema: &RowSchema,
22 qualifier: &str,
23 column_aliases: &[String],
24) -> Result<RowSchema, SQLError> {
25 if column_aliases.len() > schema.len() {
26 return Err(SQLError::Routine {
27 sqlstate: "42P10".into(),
28 message: format!(
29 "table \"{qualifier}\" has {} columns available but {} columns specified",
30 schema.len(),
31 column_aliases.len()
32 ),
33 });
34 }
35 Ok(rename_schema(schema, column_aliases, Some(qualifier)))
36}
37
38impl SchemaScope {
39 pub(super) fn alias_relation_schema(
40 &self,
41 schema: &RowSchema,
42 name: &str,
43 qualifier: &str,
44 column_aliases: &[String],
45 ) -> Result<RowSchema, SQLError> {
46 let schema = alias_table_schema(schema, qualifier, column_aliases)?;
47 match self.catalog.relation_row_type(&self.resolution, name)? {
48 Some(row_type) => {
49 schema.with_relation_row_type(qualifier, crate::ColumnType::Composite(row_type))
50 }
51 None => Ok(schema),
52 }
53 }
54}
55
56pub(super) struct JoinSchemaBinding<'a> {
57 pub(super) routines: &'a dyn RoutineResolution,
58 pub(super) kind: JoinKind,
59 pub(super) on: Option<&'a ScalarExpr>,
60 pub(super) using: Option<&'a JoinUsing>,
61 pub(super) natural: bool,
62 pub(super) alias: Option<&'a str>,
63 pub(super) column_aliases: &'a [String],
64 pub(super) left: &'a RowSchema,
65 pub(super) right: &'a RowSchema,
66 pub(super) subqueries: &'a [QueryPlan],
67 pub(super) params: &'a [SQLParam],
68 pub(super) outer: Option<&'a RowSchema>,
69}
70
71pub(super) fn table_function_member_source(function: &TableFunctionPlan) -> SourcePlan {
72 SourcePlan::Function {
73 name: function.name.clone(),
74 binding: function.binding.clone(),
75 output_name: function.output_name.clone(),
76 relations: function.relations.clone(),
77 args: function.args.clone(),
78 alias: None,
79 column_aliases: function.column_aliases.clone(),
80 ordinality: false,
81 column_types: function.column_types.clone(),
82 }
83}
84
85pub fn bind_source_plan_schema(
87 routines: &dyn RoutineResolution,
88 source: &SourcePlan,
89 params: &[SQLParam],
90 ctes: &BindingContext,
91 outer: Option<&RowSchema>,
92) -> Result<RowSchema, SQLError> {
93 SchemaScope::from_context(ctes)?.bind_source(
94 routines,
95 source,
96 ctes.scalar_subqueries,
97 params,
98 outer,
99 )
100}
101
102pub fn with_query_table_pseudo_columns(schema: &RowSchema) -> RowSchema {
104 analysis::with_unqualified_table_pseudo_columns(schema)
105}
106
107pub fn analyze_source_plan_schema(
109 routines: &dyn RoutineResolution,
110 source: &SourcePlan,
111 params: &[SQLParam],
112 ctes: &BindingContext,
113 outer: Option<&RowSchema>,
114) -> Result<RowSchema, SQLError> {
115 SchemaScope::for_analysis(ctes)?.bind_source(
116 routines,
117 source,
118 ctes.scalar_subqueries,
119 params,
120 outer,
121 )
122}
123
124pub fn bind_source_plan_schema_for_execution(
126 routines: &dyn RoutineResolution,
127 source: &mut SourcePlan,
128 params: &[SQLParam],
129 ctes: &BindingContext,
130 outer: Option<&RowSchema>,
131) -> Result<RowSchema, SQLError> {
132 SchemaScope::from_context(ctes)?.bind_source_for_execution(
133 routines,
134 source,
135 ctes.scalar_subqueries,
136 params,
137 outer,
138 )
139}
140
141impl SchemaScope {
142 pub(super) fn bind_source(
143 &mut self,
144 routines: &dyn RoutineResolution,
145 source: &SourcePlan,
146 subqueries: &[QueryPlan],
147 params: &[SQLParam],
148 outer: Option<&RowSchema>,
149 ) -> Result<RowSchema, SQLError> {
150 let bound = matches!(
151 source,
152 SourcePlan::Table {
153 bound_columns: Some(_),
154 ..
155 }
156 );
157 let previous = bound.then(|| {
158 self.resolution
159 .set_lookup_mode(crate::catalog::resolution::RelationLookupMode::Bound)
160 });
161 let result = self.bind_source_inner(routines, source, subqueries, params, outer);
162 if let Some(previous) = previous {
163 self.resolution.set_lookup_mode(previous);
164 }
165 result
166 }
167}
168
169impl SchemaScope {
170 pub(super) fn bind_source_for_execution(
171 &mut self,
172 routines: &dyn RoutineResolution,
173 source: &mut SourcePlan,
174 subqueries: &[QueryPlan],
175 params: &[SQLParam],
176 outer: Option<&RowSchema>,
177 ) -> Result<RowSchema, SQLError> {
178 match source {
179 SourcePlan::Join {
180 left,
181 right,
182 kind,
183 on,
184 using,
185 natural,
186 alias,
187 column_aliases,
188 lateral,
189 ..
190 } => {
191 let left_schema =
192 self.bind_source_for_execution(routines, left, subqueries, params, outer)?;
193 let implicit_lateral_function = matches!(
194 right.as_ref(),
195 SourcePlan::Function { .. } | SourcePlan::FunctionGroup { .. }
196 );
197 let right_scope = (*lateral || implicit_lateral_function)
198 .then(|| overlay_outer_schema(&left_schema, outer));
199 let right_schema = self.bind_source_for_execution(
200 routines,
201 right,
202 subqueries,
203 params,
204 right_scope.as_ref().or(outer),
205 )?;
206 return self.bind_join_output_schema(JoinSchemaBinding {
207 routines,
208 kind: *kind,
209 on: on.as_ref(),
210 using: using.as_ref(),
211 natural: *natural,
212 alias: alias.as_deref(),
213 column_aliases,
214 left: &left_schema,
215 right: &right_schema,
216 subqueries,
217 params,
218 outer,
219 });
220 }
221 SourcePlan::Function {
222 name,
223 binding,
224 relations,
225 args,
226 ..
227 } => {
228 let lower = crate::semantics::builtin_function_dispatch_name(name);
229 if crate::registry::is_operator_join_table_function(&lower) {
230 operator_join_relation_schemas(
231 &self.catalog,
232 &self.resolution,
233 relations.as_ref(),
234 )?;
235 return self.bind_source(routines, source, subqueries, params, outer);
236 }
237 let input = outer.cloned().unwrap_or_default();
238 let resolver = self.query_function_type_resolver_for_subqueries(
239 routines, args, &input, subqueries, params,
240 )?;
241 if let Some(selected) = crate::semantics::resolve_table_function_binding(
242 routines,
243 name,
244 binding.as_ref(),
245 args,
246 &input,
247 params,
248 &resolver,
249 )? {
250 *binding = Some(selected);
251 }
252 }
253 SourcePlan::FunctionGroup { functions, .. } => {
254 for function in functions {
255 let mut member = table_function_member_source(function);
256 self.bind_source_for_execution(
257 routines,
258 &mut member,
259 subqueries,
260 params,
261 outer,
262 )?;
263 let SourcePlan::Function { binding, .. } = member else {
264 unreachable!("table-function member changed source kind during binding")
265 };
266 function.binding = binding;
267 }
268 }
269 SourcePlan::Table { .. } | SourcePlan::Values { .. } | SourcePlan::Subquery { .. } => {}
270 }
271 self.bind_source(routines, source, subqueries, params, outer)
272 }
273}