1use crate::ast::ColumnType;
10use crate::plan::{ProjectionPlan, QueryBlockPlan, QueryPlan};
11use crate::RowSchema;
12use crate::{SQLError, SQLParam};
13
14use super::{
15 bind_query_plan_schema, projection_columns, BindingContext, QueryFunctionTypeResolver,
16 ScalarExpr, SchemaScope,
17};
18use crate::routines::RoutineResolution;
19
20type ProjectionStarColumn = (String, Option<ColumnType>);
21
22pub fn bind_projection_output_schema(
24 routines: &dyn RoutineResolution,
25 projections: &[ProjectionPlan],
26 expression_schema: &RowSchema,
27 star_schema: &RowSchema,
28 subqueries: &[QueryPlan],
29 params: &[SQLParam],
30 ctes: &BindingContext,
31) -> Result<RowSchema, SQLError> {
32 projection_output_schema(
33 SchemaScope::from_context(ctes)?,
34 routines,
35 projections,
36 expression_schema,
37 star_schema,
38 subqueries,
39 params,
40 )
41}
42
43pub fn analyze_projection_output_schema(
45 routines: &dyn RoutineResolution,
46 projections: &[ProjectionPlan],
47 expression_schema: &RowSchema,
48 star_schema: &RowSchema,
49 subqueries: &[QueryPlan],
50 params: &[SQLParam],
51 ctes: &BindingContext,
52) -> Result<RowSchema, SQLError> {
53 projection_output_schema(
54 SchemaScope::for_analysis(ctes)?,
55 routines,
56 projections,
57 expression_schema,
58 star_schema,
59 subqueries,
60 params,
61 )
62}
63
64fn projection_output_schema(
65 mut scope: SchemaScope,
66 routines: &dyn RoutineResolution,
67 projections: &[ProjectionPlan],
68 expression_schema: &RowSchema,
69 star_schema: &RowSchema,
70 subqueries: &[QueryPlan],
71 params: &[SQLParam],
72) -> Result<RowSchema, SQLError> {
73 let labels = projection_columns(projections);
74 let mut columns = Vec::new();
75 let mut types = Vec::new();
76 let mut records = Vec::new();
77 for (position, projection) in projections.iter().enumerate() {
78 let expansion_schema = match projection.expr {
79 ScalarExpr::QualifiedStar(_) => expression_schema,
80 _ => star_schema,
81 };
82 if let Some(star_columns) = projection_star_columns(&projection.expr, expansion_schema)? {
83 records.extend(
84 super::record_fields::star_fields(&projection.expr, expansion_schema)
85 .unwrap_or_default(),
86 );
87 for (column, ty) in star_columns {
88 columns.push(column);
89 types.push(ty);
90 }
91 continue;
92 }
93 columns.push(labels[position].clone());
94 types.push(scope.bind_expression_type(
95 routines,
96 &projection.expr,
97 expression_schema,
98 subqueries,
99 params,
100 )?);
101 records.push(
102 if types
103 .last()
104 .is_some_and(|ty| matches!(ty, Some(ColumnType::Record | ColumnType::Composite(_))))
105 {
106 scope.bind_record_fields(
107 routines,
108 &projection.expr,
109 expression_schema,
110 subqueries,
111 params,
112 )?
113 } else {
114 None
115 },
116 );
117 }
118 Ok(RowSchema::with_types(columns, types).with_record_fields(
119 records
120 .into_iter()
121 .enumerate()
122 .filter_map(|(index, fields)| fields.map(|fields| (index, fields))),
123 ))
124}
125
126pub fn validate_query_block_expression_types(
128 routines: &dyn RoutineResolution,
129 statement: &QueryBlockPlan,
130 schema: &RowSchema,
131 params: &[SQLParam],
132 ctes: &BindingContext,
133) -> Result<(), SQLError> {
134 let scalar_subquery_types = statement
135 .subqueries
136 .iter()
137 .map(|plan| {
138 bind_query_plan_schema(routines, plan, params, ctes, Some(schema))
139 .map(|output| output.column_type(0).cloned())
140 })
141 .collect::<Result<Vec<_>, _>>()?;
142 let resolver = QueryFunctionTypeResolver {
143 routines,
144 scalar_subquery_types: Some(scalar_subquery_types),
145 defer_routine_namespace_errors: true,
146 };
147 for expression in statement
148 .projections
149 .iter()
150 .map(|projection| &projection.expr)
151 .chain(statement.group_by.iter())
152 .chain(statement.grouping_sets.iter().flatten())
153 .chain(statement.order_by.iter().map(|order| &order.expr))
154 .chain(statement.distinct_on.iter())
155 .chain(statement.r#where.iter())
156 .chain(statement.having.iter())
157 .chain(statement.limit.iter())
158 .chain(statement.offset.iter())
159 {
160 crate::scalar_type_with_resolver(expression, schema, params, &resolver)?;
161 }
162 for expression in statement
163 .group_by
164 .iter()
165 .chain(statement.grouping_sets.iter().flatten())
166 .chain(statement.distinct_on.iter())
167 {
168 if let Some(ty) = crate::scalar_type_with_resolver(expression, schema, params, &resolver)? {
169 crate::require_equality_operator(&ty)?;
170 }
171 }
172 for order in &statement.order_by {
173 if let Some(ty) = crate::scalar_type_with_resolver(&order.expr, schema, params, &resolver)?
174 {
175 crate::require_ordering_operator(&ty)?;
176 }
177 }
178 Ok(())
179}
180
181pub fn validate_query_block_references(
183 routines: &dyn RoutineResolution,
184 statement: &QueryBlockPlan,
185 schema: &RowSchema,
186 params: &[SQLParam],
187 ctes: &BindingContext,
188 outer: Option<&RowSchema>,
189) -> Result<(), SQLError> {
190 let output = analyze_projection_output_schema(
191 routines,
192 &statement.projections,
193 schema,
194 schema,
195 &statement.subqueries,
196 params,
197 ctes,
198 )?;
199 if statement.distinct && statement.distinct_on.is_empty() {
200 for ty in output.column_types().iter().flatten() {
201 crate::require_equality_operator(ty)?;
202 }
203 }
204 SchemaScope::for_analysis(ctes)?
205 .validate_query_block_clauses(routines, statement, schema, &output, params, outer)
206}
207
208pub(super) fn projection_star_columns(
209 expression: &ScalarExpr,
210 schema: &RowSchema,
211) -> Result<Option<Vec<ProjectionStarColumn>>, SQLError> {
212 match expression {
213 ScalarExpr::Star => Ok(Some(
214 schema
215 .columns()
216 .iter()
217 .enumerate()
218 .map(|(position, column)| {
219 (
220 schema.public_name(position).unwrap_or(column).to_string(),
221 schema.column_type(position).cloned(),
222 )
223 })
224 .collect(),
225 )),
226 ScalarExpr::QualifiedStar(qualifier) => {
227 let columns = schema
228 .qualified_star_layout(qualifier)
229 .into_iter()
230 .map(|(column, _, ty)| (column, ty))
231 .collect::<Vec<_>>();
232 if columns.is_empty() {
233 return Err(SQLError::UnknownTable(qualifier.clone()));
234 }
235 Ok(Some(columns))
236 }
237 _ => Ok(None),
238 }
239}
240
241pub(super) fn rename_schema(
242 schema: &RowSchema,
243 aliases: &[String],
244 qualifier: Option<&str>,
245) -> RowSchema {
246 let columns = schema
247 .columns()
248 .iter()
249 .enumerate()
250 .map(|(position, column)| {
251 aliases
252 .get(position)
253 .cloned()
254 .unwrap_or_else(|| schema.public_name(position).unwrap_or(column).to_string())
255 })
256 .collect();
257 let renamed = match qualifier {
258 Some(qualifier) => {
259 RowSchema::with_qualified_types(qualifier, columns, schema.column_types().to_vec())
260 }
261 None => RowSchema::with_types(columns, schema.column_types().to_vec()),
262 };
263 let renamed = renamed.with_record_fields_from(schema);
264 let renamed = if schema.columns_are_open(None) {
265 RowSchema::with_open_columns(&renamed, qualifier)
266 } else {
267 renamed
268 };
269 let mut hidden = Vec::new();
270 let mut conflicting = Vec::new();
271 for (identity, ty) in schema.typed_virtual_identities() {
272 let conflicts = match identity.qualifier() {
273 Some(source) => schema.qualified_column_is_ambiguous(source, identity.column()),
274 None => schema.column_is_ambiguous(identity.column()),
275 };
276 let mapped = qualifier.map_or_else(
277 || vec![identity.clone()],
278 |qualifier| {
279 vec![
280 crate::ColumnIdentity::unqualified(identity.column()),
281 crate::ColumnIdentity::qualified(qualifier, identity.column()),
282 ]
283 },
284 );
285 for identity in mapped {
286 if conflicts {
287 conflicting.push((identity, ty.cloned()));
288 } else {
289 hidden.push((identity, ty.cloned()));
290 }
291 }
292 }
293 let renamed = RowSchema::with_typed_virtual_identities(&renamed, &hidden);
294 RowSchema::with_typed_conflicting_virtual_identities(&renamed, &conflicting)
295}