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>, Option<u32>);
21
22pub(super) struct ProjectionMetadata {
23 pub ty: Option<ColumnType>,
24 pub collation: Option<u32>,
25 pub record: Option<crate::schema::RecordMetadata>,
26}
27
28#[derive(Default)]
30pub(super) struct OutputCollations(Vec<(usize, u32)>);
31
32impl OutputCollations {
33 pub(super) fn push(&mut self, position: usize, ty: Option<&ColumnType>, oid: Option<u32>) {
34 if let Some((ty, oid)) = ty.zip(oid) {
35 if crate::schema::composites::type_is_collatable(ty)
36 && crate::catalog::type_metadata::pg_type_collation_oid(ty) != i64::from(oid)
37 {
38 self.0.push((position, oid));
39 }
40 }
41 }
42
43 pub(super) fn apply(self, mut schema: RowSchema) -> RowSchema {
44 for (position, oid) in self.0 {
45 schema = schema
46 .with_collation_override(crate::schema::CollationBinding::Column(position), oid)
47 .expect("selected collation belongs to its typed output");
48 }
49 schema
50 }
51}
52
53pub fn bind_projection_output_schema(
55 routines: &dyn RoutineResolution,
56 projections: &[ProjectionPlan],
57 expression_schema: &RowSchema,
58 star_schema: &RowSchema,
59 subqueries: &[QueryPlan],
60 params: &[SQLParam],
61 ctes: &BindingContext,
62) -> Result<RowSchema, SQLError> {
63 projection_output_schema(
64 SchemaScope::from_context(ctes)?,
65 routines,
66 projections,
67 expression_schema,
68 star_schema,
69 subqueries,
70 params,
71 )
72}
73
74pub fn analyze_projection_output_schema(
76 routines: &dyn RoutineResolution,
77 projections: &[ProjectionPlan],
78 expression_schema: &RowSchema,
79 star_schema: &RowSchema,
80 subqueries: &[QueryPlan],
81 params: &[SQLParam],
82 ctes: &BindingContext,
83) -> Result<RowSchema, SQLError> {
84 projection_output_schema(
85 SchemaScope::for_analysis(ctes)?,
86 routines,
87 projections,
88 expression_schema,
89 star_schema,
90 subqueries,
91 params,
92 )
93}
94
95fn projection_output_schema(
96 mut scope: SchemaScope,
97 routines: &dyn RoutineResolution,
98 projections: &[ProjectionPlan],
99 expression_schema: &RowSchema,
100 star_schema: &RowSchema,
101 subqueries: &[QueryPlan],
102 params: &[SQLParam],
103) -> Result<RowSchema, SQLError> {
104 let labels = projection_columns(projections);
105 let mut columns = Vec::new();
106 let mut types = Vec::new();
107 let mut records = Vec::new();
108 let mut collations = OutputCollations::default();
109 for (position, projection) in projections.iter().enumerate() {
110 let expansion_schema = match projection.expr {
111 ScalarExpr::QualifiedStar(_) => expression_schema,
112 _ => star_schema,
113 };
114 if let Some(star_columns) = projection_star_columns(&projection.expr, expansion_schema)? {
115 records.extend(
116 super::record_fields::star_metadata(&projection.expr, expansion_schema)
117 .unwrap_or_default(),
118 );
119 for (column, ty, collation) in star_columns {
120 collations.push(types.len(), ty.as_ref(), collation);
121 columns.push(column);
122 types.push(ty);
123 }
124 continue;
125 }
126 columns.push(labels[position].clone());
127 let metadata = scope.bind_projection_metadata(
128 routines,
129 &projection.expr,
130 expression_schema,
131 subqueries,
132 params,
133 )?;
134 collations.push(types.len(), metadata.ty.as_ref(), metadata.collation);
135 types.push(metadata.ty);
136 records.push(metadata.record);
137 }
138 for projection in projections {
139 scope.collations.validate_expression(&projection.expr)?;
140 }
141 Ok(collations
142 .apply(RowSchema::with_types(columns, types))
143 .with_record_metadata(
144 records
145 .into_iter()
146 .enumerate()
147 .filter_map(|(index, fields)| fields.map(|fields| (index, fields))),
148 ))
149}
150
151pub fn validate_query_block_expression_types(
153 routines: &dyn RoutineResolution,
154 statement: &QueryBlockPlan,
155 schema: &RowSchema,
156 params: &[SQLParam],
157 ctes: &BindingContext,
158) -> Result<(), SQLError> {
159 let scalar_subquery_types = statement
160 .subqueries
161 .iter()
162 .map(|plan| {
163 bind_query_plan_schema(routines, plan, params, ctes, Some(schema))
164 .map(|output| output.column_type(0).cloned())
165 })
166 .collect::<Result<Vec<_>, _>>()?;
167 let resolver = QueryFunctionTypeResolver {
168 routines,
169 scalar_subquery_types: Some(scalar_subquery_types),
170 defer_routine_namespace_errors: true,
171 };
172 for expression in statement
173 .projections
174 .iter()
175 .map(|projection| &projection.expr)
176 .chain(statement.group_by.iter())
177 .chain(statement.grouping_sets.iter().flatten())
178 .chain(statement.order_by.iter().map(|order| &order.expr))
179 .chain(statement.distinct_on.iter())
180 .chain(statement.r#where.iter())
181 .chain(statement.having.iter())
182 .chain(statement.limit.iter())
183 .chain(statement.offset.iter())
184 {
185 crate::scalar_type_with_resolver(expression, schema, params, &resolver)?;
186 }
187 for expression in statement
188 .group_by
189 .iter()
190 .chain(statement.grouping_sets.iter().flatten())
191 .chain(statement.distinct_on.iter())
192 {
193 if let Some(ty) = crate::scalar_type_with_resolver(expression, schema, params, &resolver)? {
194 crate::type_resolution::ComparisonSupport::Equality
195 .require_in_catalog(&ty, routines)?;
196 }
197 }
198 for order in &statement.order_by {
199 if let Some(ty) = crate::scalar_type_with_resolver(&order.expr, schema, params, &resolver)?
200 {
201 crate::type_resolution::ComparisonSupport::Ordering
202 .require_in_catalog(&ty, routines)?;
203 }
204 }
205 Ok(())
206}
207
208pub fn validate_query_block_references(
210 routines: &dyn RoutineResolution,
211 statement: &QueryBlockPlan,
212 schema: &RowSchema,
213 params: &[SQLParam],
214 ctes: &BindingContext,
215 outer: Option<&RowSchema>,
216) -> Result<(), SQLError> {
217 let output = analyze_projection_output_schema(
218 routines,
219 &statement.projections,
220 schema,
221 schema,
222 &statement.subqueries,
223 params,
224 ctes,
225 )?;
226 if statement.distinct && statement.distinct_on.is_empty() {
227 for ty in output.column_types().iter().flatten() {
228 crate::type_resolution::ComparisonSupport::Equality.require_in_catalog(ty, routines)?;
229 }
230 }
231 SchemaScope::for_analysis(ctes)?
232 .validate_query_block_clauses(routines, statement, schema, &output, params, outer)
233}
234
235pub(super) fn projection_star_columns(
236 expression: &ScalarExpr,
237 schema: &RowSchema,
238) -> Result<Option<Vec<ProjectionStarColumn>>, SQLError> {
239 match expression {
240 ScalarExpr::Star => Ok(Some(
241 schema
242 .columns()
243 .iter()
244 .enumerate()
245 .map(|(position, column)| {
246 (
247 schema.public_name(position).unwrap_or(column).to_string(),
248 schema.column_type(position).cloned(),
249 schema.column_collation(position),
250 )
251 })
252 .collect(),
253 )),
254 ScalarExpr::QualifiedStar(qualifier) => {
255 let columns = schema
256 .qualified_star_position_layout(qualifier)
257 .into_iter()
258 .map(|(column, logical, _, ty)| {
259 let collation = logical.map_or_else(
260 || schema.qualified_collation(qualifier, &column),
261 |position| schema.column_collation(position),
262 );
263 (column, ty, collation)
264 })
265 .collect::<Vec<_>>();
266 if columns.is_empty() {
267 return Err(SQLError::UnknownTable(qualifier.clone()));
268 }
269 Ok(Some(columns))
270 }
271 _ => Ok(None),
272 }
273}
274
275pub(super) fn rename_schema(
276 schema: &RowSchema,
277 aliases: &[String],
278 qualifier: Option<&str>,
279) -> RowSchema {
280 let columns = schema
281 .columns()
282 .iter()
283 .enumerate()
284 .map(|(position, column)| {
285 aliases
286 .get(position)
287 .cloned()
288 .unwrap_or_else(|| schema.public_name(position).unwrap_or(column).to_string())
289 })
290 .collect();
291 let renamed = match qualifier {
292 Some(qualifier) => {
293 RowSchema::with_qualified_types(qualifier, columns, schema.column_types().to_vec())
294 }
295 None => RowSchema::with_types(columns, schema.column_types().to_vec()),
296 };
297 let renamed = renamed
298 .with_column_collations((0..schema.len()).map(|position| schema.column_collation(position)))
299 .with_record_fields_from(schema);
300 let renamed = if schema.columns_are_open(None) {
301 RowSchema::with_open_columns(&renamed, qualifier)
302 } else {
303 renamed
304 };
305 let mut hidden = Vec::new();
306 let mut conflicting = Vec::new();
307 for (identity, ty) in schema.typed_virtual_identities() {
308 let conflicts = match identity.qualifier() {
309 Some(source) => schema.qualified_column_is_ambiguous(source, identity.column()),
310 None => schema.column_is_ambiguous(identity.column()),
311 };
312 let mapped = qualifier.map_or_else(
313 || vec![identity.clone()],
314 |qualifier| {
315 vec![
316 crate::ColumnIdentity::unqualified(identity.column()),
317 crate::ColumnIdentity::qualified(qualifier, identity.column()),
318 ]
319 },
320 );
321 for identity in mapped {
322 if conflicts {
323 conflicting.push((identity, ty.cloned()));
324 } else {
325 hidden.push((identity, ty.cloned()));
326 }
327 }
328 }
329 let renamed = RowSchema::with_typed_virtual_identities(&renamed, &hidden);
330 RowSchema::with_typed_conflicting_virtual_identities(&renamed, &conflicting)
331}