1use crate::dialects::DialectType;
13use crate::expressions::{Expression, Identifier, TableRef};
14#[cfg(feature = "generate")]
15use crate::generator::Generator;
16use crate::schema::{normalize_name, Schema};
17use crate::scope::{Scope, SourceInfo, SourceKind};
18use crate::traversal::ExpressionWalk;
19use std::collections::{HashMap, HashSet};
20use thiserror::Error;
21
22#[derive(Debug, Error, Clone)]
24pub enum ResolverError {
25 #[error("Unknown table: {0}")]
26 UnknownTable(String),
27
28 #[error("Ambiguous column: {column} appears in multiple sources: {sources}")]
29 AmbiguousColumn { column: String, sources: String },
30
31 #[error("Column not found: {0}")]
32 ColumnNotFound(String),
33
34 #[error("Unknown set operation: {0}")]
35 UnknownSetOperation(String),
36
37 #[error("Invalid set operation: {0}")]
38 InvalidSetOperation(String),
39}
40
41pub type ResolverResult<T> = Result<T, ResolverError>;
43
44pub struct Resolver<'a> {
49 pub scope: &'a Scope,
51 schema: &'a dyn Schema,
53 pub dialect: Option<DialectType>,
55 infer_schema: bool,
57 source_columns_cache: HashMap<String, Vec<String>>,
59 unambiguous_columns_cache: Option<HashMap<String, String>>,
61 all_columns_cache: Option<HashSet<String>>,
63 outer_scopes: Vec<&'a Scope>,
66}
67
68impl<'a> Resolver<'a> {
69 pub fn new(scope: &'a Scope, schema: &'a dyn Schema, infer_schema: bool) -> Self {
71 Self {
72 scope,
73 schema,
74 dialect: schema.dialect(),
75 infer_schema,
76 source_columns_cache: HashMap::new(),
77 unambiguous_columns_cache: None,
78 all_columns_cache: None,
79 outer_scopes: Vec::new(),
80 }
81 }
82
83 pub(crate) fn with_outer_scopes(mut self, scopes: &'a [Scope]) -> Self {
84 self.outer_scopes = scopes.iter().rev().collect();
85 self
86 }
87
88 pub(crate) fn outer_source_exists(&self, name: &str) -> bool {
89 self.outer_scopes.iter().any(|scope| {
90 scope.sources.keys().any(|source| {
91 normalize_name(source, self.dialect, true, true)
92 == normalize_name(name, self.dialect, true, true)
93 })
94 })
95 }
96
97 pub(crate) fn find_column_in_outer_scopes(&self, column: &str) -> Option<String> {
98 let mut shadowed = HashSet::new();
99 shadowed.extend(
100 self.scope
101 .sources
102 .keys()
103 .map(|name| normalize_name(name, self.dialect, true, true)),
104 );
105 for outer in &self.outer_scopes {
106 let mut visible = (*outer).clone();
107 visible
108 .sources
109 .retain(|name, _| shadowed.insert(normalize_name(name, self.dialect, true, true)));
110 let mut resolver = Resolver::new(&visible, self.schema, self.infer_schema);
111 if resolver.is_ambiguous(column) {
112 return None;
113 }
114 if let Some(table) = resolver.get_table(column) {
115 return Some(table);
116 }
117 }
118 None
119 }
120
121 pub fn get_table(&mut self, column_name: &str) -> Option<String> {
125 let table_name = self.get_table_name_from_sources(column_name, None);
127
128 if table_name.is_some() {
130 return table_name;
131 }
132
133 if self.infer_schema {
136 let sources_without_schema: Vec<_> = self
137 .get_all_source_columns()
138 .iter()
139 .filter(|(_, columns)| columns.is_empty() || columns.contains(&"*".to_string()))
140 .map(|(name, _)| name.clone())
141 .collect();
142
143 if sources_without_schema.len() == 1 {
144 return Some(sources_without_schema[0].clone());
145 }
146 }
147
148 None
149 }
150
151 pub fn get_table_identifier(&mut self, column_name: &str) -> Option<Identifier> {
153 self.get_table(column_name).map(Identifier::new)
154 }
155
156 pub fn table_exists_in_schema(&self, table_name: &str) -> bool {
159 self.schema.column_names(table_name).is_ok()
160 }
161
162 pub fn find_column_in_outer_schema_tables(&self, column_name: &str) -> Option<String> {
167 let tables = self.schema.find_tables_for_column(column_name);
168 let outer_tables: Vec<String> = tables
170 .into_iter()
171 .filter(|t| !self.scope.sources.contains_key(t))
172 .collect();
173 if outer_tables.len() == 1 {
175 Some(outer_tables.into_iter().next().unwrap())
176 } else {
177 None
178 }
179 }
180
181 pub fn all_columns(&mut self) -> &HashSet<String> {
183 if self.all_columns_cache.is_none() {
184 let mut all = HashSet::new();
185 for columns in self.get_all_source_columns().values() {
186 all.extend(columns.iter().cloned());
187 }
188 self.all_columns_cache = Some(all);
189 }
190 self.all_columns_cache
191 .as_ref()
192 .expect("cache populated above")
193 }
194
195 pub fn get_source_columns(&mut self, source_name: &str) -> ResolverResult<Vec<String>> {
199 if let Some(columns) = self.source_columns_cache.get(source_name) {
201 return Ok(columns.clone());
202 }
203
204 let source_info = self
206 .scope
207 .sources
208 .get(source_name)
209 .ok_or_else(|| ResolverError::UnknownTable(source_name.to_string()))?;
210
211 let columns = self.extract_columns_from_source(source_info)?;
212
213 self.source_columns_cache
215 .insert(source_name.to_string(), columns.clone());
216
217 Ok(columns)
218 }
219
220 fn extract_columns_from_source(&self, source_info: &SourceInfo) -> ResolverResult<Vec<String>> {
222 match &source_info.expression {
223 Expression::Alias(alias) if source_info.kind == SourceKind::Virtual => {
225 Ok(alias_output_columns(alias))
226 }
227 expression => self.get_source_columns_for_expression(expression),
228 }
229 }
230
231 fn get_source_columns_for_expression(
232 &self,
233 expression: &Expression,
234 ) -> ResolverResult<Vec<String>> {
235 let columns = match expression {
236 Expression::Table(table) => {
237 let table_name = qualified_table_name(table);
241 match self.schema.column_names(&table_name) {
242 Ok(cols) => cols,
243 Err(_) => Vec::new(), }
245 }
246 Expression::Subquery(subquery) => {
247 apply_alias_columns(
249 self.get_named_selects(&subquery.this),
250 &subquery.column_aliases,
251 )
252 }
253 Expression::Values(values) => self.get_values_column_names(values),
254 Expression::Select(select) => {
255 self.get_select_column_names(select)
257 }
258 Expression::Union(union) => {
259 self.get_source_columns_from_set_op(&Expression::Union(union.clone()))?
261 }
262 Expression::Intersect(intersect) => {
263 self.get_source_columns_from_set_op(&Expression::Intersect(intersect.clone()))?
264 }
265 Expression::Except(except) => {
266 self.get_source_columns_from_set_op(&Expression::Except(except.clone()))?
267 }
268 Expression::Cte(cte) => {
269 if !cte.columns.is_empty() {
270 cte.columns.iter().map(|c| c.name.clone()).collect()
271 } else {
272 self.get_named_selects(&cte.this)
273 }
274 }
275 Expression::Pivot(pivot) => self.get_pivot_output_columns(pivot),
276 Expression::Unpivot(unpivot) => self.get_unpivot_output_columns(unpivot),
277 Expression::Alias(alias) if matches!(&alias.this, Expression::Unnest(_)) => {
278 alias_output_columns(alias)
279 }
280 Expression::Alias(alias) => {
281 let columns = self.get_source_columns_for_expression(&alias.this)?;
282 apply_alias_columns(columns, &alias.column_aliases)
283 }
284 Expression::Unnest(unnest) => unnest_output_columns(unnest),
285 Expression::Lateral(lateral) => lateral_output_columns(lateral),
286 Expression::LateralView(lateral_view) => lateral_view_output_columns(lateral_view),
287 Expression::Paren(paren) => self.get_source_columns_for_expression(&paren.this)?,
288 _ => Vec::new(),
289 };
290
291 Ok(columns)
292 }
293
294 fn get_named_selects(&self, expr: &Expression) -> Vec<String> {
296 if let Ok(identifiers) = crate::set_operation::query_output_identifiers(expr, self.dialect)
297 {
298 return identifiers
299 .into_iter()
300 .map(|identifier| identifier.name)
301 .collect();
302 }
303
304 match expr {
305 Expression::Select(select) => self.get_select_column_names(select),
306 Expression::Union(union) => {
307 self.get_named_selects(&union.left)
309 }
310 Expression::Intersect(intersect) => self.get_named_selects(&intersect.left),
311 Expression::Except(except) => self.get_named_selects(&except.left),
312 Expression::Subquery(subquery) => apply_alias_columns(
313 self.get_named_selects(&subquery.this),
314 &subquery.column_aliases,
315 ),
316 Expression::Values(values) => self.get_values_column_names(values),
317 Expression::Alias(alias) => {
318 let columns = self.get_named_selects(&alias.this);
319 apply_alias_columns(columns, &alias.column_aliases)
320 }
321 Expression::Paren(paren) => self.get_named_selects(&paren.this),
322 _ => Vec::new(),
323 }
324 }
325
326 fn get_select_column_names(&self, select: &crate::expressions::Select) -> Vec<String> {
328 select
329 .expressions
330 .iter()
331 .filter_map(|expr| self.get_expression_alias(expr))
332 .collect()
333 }
334
335 fn get_values_column_names(&self, values: &crate::expressions::Values) -> Vec<String> {
336 let width = values
337 .expressions
338 .first()
339 .map_or(0, |row| row.expressions.len());
340 let prefix = match self.dialect {
341 Some(DialectType::Snowflake) => "COLUMN",
342 Some(DialectType::PostgreSQL) => "column",
343 _ => {
346 return values
347 .column_aliases
348 .iter()
349 .map(|alias| alias.name.clone())
350 .collect()
351 }
352 };
353 apply_alias_columns(
354 (1..=width)
355 .map(|ordinal| format!("{prefix}{ordinal}"))
356 .collect(),
357 &values.column_aliases,
358 )
359 }
360
361 fn get_expression_alias(&self, expr: &Expression) -> Option<String> {
363 match expr {
364 Expression::Alias(alias) => Some(alias.alias.name.clone()),
365 Expression::Column(col) => Some(col.name.name.clone()),
366 Expression::Star(_) => Some("*".to_string()),
367 Expression::Identifier(id) => Some(id.name.clone()),
368 _ => None,
369 }
370 }
371
372 fn get_pivot_output_columns(&self, pivot: &crate::expressions::Pivot) -> Vec<String> {
373 if pivot.unpivot {
374 return self.get_pivot_unpivot_output_columns(pivot);
375 }
376
377 let pre_columns = self.get_source_output_columns(&pivot.this);
378 if pre_columns.is_empty() || pre_columns.iter().any(|column| column == "*") {
379 return Vec::new();
380 }
381
382 let excluded = pivot_excluded_source_columns(pivot, self.dialect);
383 let generated = pivot_generated_output_columns(pivot, self.dialect);
384 if excluded.is_empty() || generated.is_empty() {
385 return Vec::new();
386 }
387
388 let mut columns: Vec<String> = pre_columns
389 .into_iter()
390 .filter(|column| !excluded.contains(&normalize_column_name(column, self.dialect)))
391 .collect();
392 columns.extend(generated);
393 apply_alias_columns(columns, &pivot.alias_columns)
394 }
395
396 fn get_pivot_unpivot_output_columns(&self, pivot: &crate::expressions::Pivot) -> Vec<String> {
397 let pre_columns = self.get_source_output_columns(&pivot.this);
398 if pre_columns.is_empty() || pre_columns.iter().any(|column| column == "*") {
399 return Vec::new();
400 }
401
402 let input_columns: HashSet<String> = pivot
403 .expressions
404 .iter()
405 .flat_map(expression_column_names)
406 .map(|column| normalize_column_name(&column, self.dialect))
407 .collect();
408 let mut columns: Vec<String> = pre_columns
409 .into_iter()
410 .filter(|column| !input_columns.contains(&normalize_column_name(column, self.dialect)))
411 .collect();
412
413 if let Some(Expression::UnpivotColumns(unpivot_columns)) = pivot.into.as_deref() {
414 if let Some(name) = expression_name(&unpivot_columns.this) {
415 columns.push(name);
416 }
417 for value_column in &unpivot_columns.expressions {
418 if let Some(name) = expression_name(value_column) {
419 columns.push(name);
420 }
421 }
422 }
423
424 apply_alias_columns(columns, &pivot.alias_columns)
425 }
426
427 fn get_unpivot_output_columns(&self, unpivot: &crate::expressions::Unpivot) -> Vec<String> {
428 let pre_columns = self.get_source_output_columns(&unpivot.this);
429 if pre_columns.is_empty() || pre_columns.iter().any(|column| column == "*") {
430 return Vec::new();
431 }
432
433 let input_columns: HashSet<String> = unpivot
434 .columns
435 .iter()
436 .flat_map(expression_column_names)
437 .map(|column| normalize_column_name(&column, self.dialect))
438 .collect();
439 let mut columns: Vec<String> = pre_columns
440 .into_iter()
441 .filter(|column| !input_columns.contains(&normalize_column_name(column, self.dialect)))
442 .collect();
443 columns.push(unpivot.name_column.name.clone());
444 columns.push(unpivot.value_column.name.clone());
445 columns.extend(
446 unpivot
447 .extra_value_columns
448 .iter()
449 .map(|column| column.name.clone()),
450 );
451 apply_alias_columns(columns, &unpivot.alias_columns)
452 }
453
454 fn get_source_output_columns(&self, source: &Expression) -> Vec<String> {
455 match source {
456 Expression::Table(table) => {
457 if table.schema.is_none() && table.catalog.is_none() {
458 if let Some(source) = self.scope.cte_sources.get(&table.name.name) {
459 return self.extract_columns_from_source(source).unwrap_or_default();
460 }
461 }
462
463 let table_name = qualified_table_name(table);
464 self.schema.column_names(&table_name).unwrap_or_default()
465 }
466 Expression::Subquery(subquery) => apply_alias_columns(
467 self.get_named_selects(&subquery.this),
468 &subquery.column_aliases,
469 ),
470 Expression::Values(values) => self.get_values_column_names(values),
471 Expression::Select(select) => self.get_select_column_names(select),
472 Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_) => self
473 .get_source_columns_from_set_op(source)
474 .unwrap_or_default(),
475 Expression::Alias(alias) if matches!(&alias.this, Expression::Unnest(_)) => {
476 alias_output_columns(alias)
477 }
478 Expression::Alias(alias) => {
479 let columns = self.get_source_output_columns(&alias.this);
480 apply_alias_columns(columns, &alias.column_aliases)
481 }
482 Expression::Unnest(unnest) => unnest_output_columns(unnest),
483 Expression::Lateral(lateral) => lateral_output_columns(lateral),
484 Expression::LateralView(lateral_view) => lateral_view_output_columns(lateral_view),
485 Expression::Cte(cte) => {
486 if cte.columns.is_empty() {
487 self.get_named_selects(&cte.this)
488 } else {
489 cte.columns
490 .iter()
491 .map(|column| column.name.clone())
492 .collect()
493 }
494 }
495 Expression::Paren(paren) => self.get_source_output_columns(&paren.this),
496 _ => Vec::new(),
497 }
498 }
499
500 pub fn get_source_columns_from_set_op(
502 &self,
503 expression: &Expression,
504 ) -> ResolverResult<Vec<String>> {
505 if matches!(
506 expression,
507 Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
508 ) {
509 match crate::set_operation::set_operation_layout(expression, self.dialect) {
510 Ok(Some(layout)) => {
511 return Ok(layout
512 .outputs
513 .into_iter()
514 .map(|output| output.identifier.name)
515 .collect())
516 }
517 Ok(None) => {}
518 Err(error) if error.is_indeterminate() => {}
519 Err(error) => return Err(ResolverError::InvalidSetOperation(error.to_string())),
520 }
521 }
522
523 match expression {
524 Expression::Select(select) => Ok(self.get_select_column_names(select)),
525 Expression::Subquery(subquery) => {
526 if matches!(
527 &subquery.this,
528 Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
529 ) {
530 self.get_source_columns_from_set_op(&subquery.this)
531 } else {
532 Ok(self.get_named_selects(&subquery.this))
533 }
534 }
535 Expression::Alias(alias) => {
536 let columns = self.get_source_columns_from_set_op(&alias.this)?;
537 Ok(apply_alias_columns(columns, &alias.column_aliases))
538 }
539 Expression::Paren(paren) => self.get_source_columns_from_set_op(&paren.this),
540 Expression::Union(union) => {
541 self.get_source_columns_from_set_op(&union.left)
543 }
544 Expression::Intersect(intersect) => {
545 self.get_source_columns_from_set_op(&intersect.left)
546 }
547 Expression::Except(except) => self.get_source_columns_from_set_op(&except.left),
548 _ => Err(ResolverError::UnknownSetOperation(format!(
549 "{:?}",
550 expression
551 ))),
552 }
553 }
554
555 fn get_all_source_columns(&mut self) -> HashMap<String, Vec<String>> {
557 let source_names: Vec<_> = self.scope.sources.keys().cloned().collect();
558
559 let mut result = HashMap::new();
560 for source_name in source_names {
561 if let Ok(columns) = self.get_source_columns(&source_name) {
562 result.insert(source_name, columns);
563 }
564 }
565 result
566 }
567
568 fn get_table_name_from_sources(
570 &mut self,
571 column_name: &str,
572 source_columns: Option<&HashMap<String, Vec<String>>>,
573 ) -> Option<String> {
574 let normalized_column_name = normalize_column_name(column_name, self.dialect);
575 let unambiguous = match source_columns {
576 Some(cols) => self.compute_unambiguous_columns(cols),
577 None => {
578 if self.unambiguous_columns_cache.is_none() {
579 let all_source_columns = self.get_all_source_columns();
580 self.unambiguous_columns_cache =
581 Some(self.compute_unambiguous_columns(&all_source_columns));
582 }
583 self.unambiguous_columns_cache
584 .clone()
585 .expect("cache populated above")
586 }
587 };
588
589 unambiguous.get(&normalized_column_name).cloned()
590 }
591
592 fn compute_unambiguous_columns(
596 &self,
597 source_columns: &HashMap<String, Vec<String>>,
598 ) -> HashMap<String, String> {
599 if source_columns.is_empty() {
600 return HashMap::new();
601 }
602
603 let mut column_to_sources: HashMap<String, Vec<String>> = HashMap::new();
604
605 for (source_name, columns) in source_columns {
606 for column in columns {
607 column_to_sources
608 .entry(normalize_column_name(column, self.dialect))
609 .or_default()
610 .push(source_name.clone());
611 }
612 }
613
614 column_to_sources
616 .into_iter()
617 .filter(|(_, sources)| sources.len() == 1)
618 .map(|(column, sources)| (column, sources.into_iter().next().unwrap()))
619 .collect()
620 }
621
622 pub fn is_ambiguous(&mut self, column_name: &str) -> bool {
624 let normalized_column_name = normalize_column_name(column_name, self.dialect);
625 let all_source_columns = self.get_all_source_columns();
626 let sources_with_column: Vec<_> = all_source_columns
627 .iter()
628 .filter(|(_, columns)| {
629 columns.iter().any(|column| {
630 normalize_column_name(column, self.dialect) == normalized_column_name
631 })
632 })
633 .map(|(name, _)| name.clone())
634 .collect();
635
636 sources_with_column.len() > 1
637 }
638
639 pub fn sources_for_column(&mut self, column_name: &str) -> Vec<String> {
641 let normalized_column_name = normalize_column_name(column_name, self.dialect);
642 let all_source_columns = self.get_all_source_columns();
643 all_source_columns
644 .iter()
645 .filter(|(_, columns)| {
646 columns.iter().any(|column| {
647 normalize_column_name(column, self.dialect) == normalized_column_name
648 })
649 })
650 .map(|(name, _)| name.clone())
651 .collect()
652 }
653
654 pub fn disambiguate_in_join_context(
659 &mut self,
660 column_name: &str,
661 available_sources: &[String],
662 ) -> Option<String> {
663 let normalized_column_name = normalize_column_name(column_name, self.dialect);
664 let mut matching_sources = Vec::new();
665
666 for source_name in available_sources {
667 if let Ok(columns) = self.get_source_columns(source_name) {
668 if columns.iter().any(|column| {
669 normalize_column_name(column, self.dialect) == normalized_column_name
670 }) {
671 matching_sources.push(source_name.clone());
672 }
673 }
674 }
675
676 if matching_sources.len() == 1 {
677 Some(matching_sources.remove(0))
678 } else {
679 None
680 }
681 }
682}
683
684fn normalize_column_name(name: &str, dialect: Option<DialectType>) -> String {
685 normalize_name(name, dialect, false, true)
686}
687
688fn apply_alias_columns(mut columns: Vec<String>, alias_columns: &[Identifier]) -> Vec<String> {
689 for (idx, alias) in alias_columns.iter().enumerate() {
690 if let Some(column) = columns.get_mut(idx) {
691 *column = alias.name.clone();
692 }
693 }
694 columns
695}
696
697fn unnest_output_columns(unnest: &crate::expressions::UnnestFunc) -> Vec<String> {
698 unnest
699 .alias
700 .iter()
701 .map(|alias| alias.name.clone())
702 .chain(unnest.offset_alias.iter().map(|alias| alias.name.clone()))
703 .collect()
704}
705
706fn alias_output_columns(alias: &crate::expressions::Alias) -> Vec<String> {
707 if alias.column_aliases.is_empty() {
708 vec![alias.alias.name.clone()]
709 } else {
710 alias
711 .column_aliases
712 .iter()
713 .map(|column| column.name.clone())
714 .collect()
715 }
716}
717
718fn lateral_output_columns(lateral: &crate::expressions::Lateral) -> Vec<String> {
719 if lateral.column_aliases.is_empty() {
720 default_virtual_output_columns(&lateral.this)
721 } else {
722 lateral.column_aliases.clone()
723 }
724}
725
726fn lateral_view_output_columns(lateral_view: &crate::expressions::LateralView) -> Vec<String> {
727 lateral_view
728 .column_aliases
729 .iter()
730 .map(|column| column.name.clone())
731 .collect()
732}
733
734fn default_virtual_output_columns(expression: &Expression) -> Vec<String> {
735 match expression {
736 Expression::Unnest(unnest) => unnest_output_columns(unnest),
737 Expression::Alias(alias) if matches!(&alias.this, Expression::Unnest(_)) => {
738 alias_output_columns(alias)
739 }
740 Expression::Function(function) if function.name.eq_ignore_ascii_case("FLATTEN") => {
741 ["seq", "key", "path", "index", "value", "this"]
742 .into_iter()
743 .map(String::from)
744 .collect()
745 }
746 _ => Vec::new(),
747 }
748}
749
750fn pivot_excluded_source_columns(
751 pivot: &crate::expressions::Pivot,
752 dialect: Option<DialectType>,
753) -> HashSet<String> {
754 pivot
755 .fields
756 .iter()
757 .chain(pivot.expressions.iter())
758 .chain(pivot.using.iter())
759 .flat_map(expression_column_names)
760 .map(|column| normalize_column_name(&column, dialect))
761 .collect()
762}
763
764fn pivot_generated_output_columns(
765 pivot: &crate::expressions::Pivot,
766 _dialect: Option<DialectType>,
767) -> Vec<String> {
768 let fields = pivot_field_output_names(pivot);
769 let aggregations = if pivot.using.is_empty() {
770 &pivot.expressions
771 } else {
772 &pivot.using
773 };
774
775 if fields.is_empty() || aggregations.is_empty() {
776 return Vec::new();
777 }
778
779 let needs_suffix = aggregations.len() > 1;
780 let mut outputs = Vec::new();
781 for field in fields {
782 for aggregation in aggregations {
783 if let Some(suffix) = pivot_aggregation_output_suffix(aggregation, needs_suffix) {
784 outputs.push(format!("{field}_{suffix}"));
785 } else {
786 outputs.push(field.clone());
787 }
788 }
789 }
790 outputs
791}
792
793fn pivot_field_output_names(pivot: &crate::expressions::Pivot) -> Vec<String> {
794 pivot
795 .fields
796 .iter()
797 .filter_map(|field| match field {
798 Expression::In(in_expr) => Some(
799 in_expr
800 .expressions
801 .iter()
802 .filter_map(expression_name)
803 .collect::<Vec<_>>(),
804 ),
805 _ => None,
806 })
807 .flatten()
808 .collect()
809}
810
811fn pivot_aggregation_output_suffix(expr: &Expression, needs_suffix: bool) -> Option<String> {
812 match expr {
813 Expression::Alias(alias) => Some(alias.alias.name.clone()),
814 _ if needs_suffix => pivot_generated_aggregation_suffix(expr),
815 _ => None,
816 }
817}
818
819#[cfg(feature = "generate")]
820fn pivot_generated_aggregation_suffix(expr: &Expression) -> Option<String> {
821 Generator::sql(expr).ok().map(|sql| sql.to_lowercase())
822}
823
824#[cfg(not(feature = "generate"))]
825fn pivot_generated_aggregation_suffix(expr: &Expression) -> Option<String> {
826 expression_name(expr).or_else(|| Some(expr.variant_name().to_string()))
827}
828
829fn expression_name(expr: &Expression) -> Option<String> {
830 match expr {
831 Expression::PivotAlias(alias) => expression_name(&alias.alias),
832 Expression::Alias(alias) => Some(alias.alias.name.clone()),
833 Expression::Identifier(identifier) => Some(identifier.name.clone()),
834 Expression::Column(column) => Some(column.name.name.clone()),
835 Expression::Literal(literal) => Some(literal.value_str().to_string()),
836 Expression::Var(var) => Some(var.this.clone()),
837 Expression::Tuple(tuple) => tuple.expressions.first().and_then(expression_name),
838 _ => None,
839 }
840}
841
842fn expression_column_names(expr: &Expression) -> Vec<String> {
843 expr.find_all(|node| matches!(node, Expression::Column(_)))
844 .into_iter()
845 .filter_map(|node| match node {
846 Expression::Column(column) => Some(column.name.name.clone()),
847 _ => None,
848 })
849 .collect()
850}
851
852pub fn resolve_column(
856 scope: &Scope,
857 schema: &dyn Schema,
858 column_name: &str,
859 infer_schema: bool,
860) -> Option<String> {
861 let mut resolver = Resolver::new(scope, schema, infer_schema);
862 resolver.get_table(column_name)
863}
864
865pub fn is_column_ambiguous(scope: &Scope, schema: &dyn Schema, column_name: &str) -> bool {
867 let mut resolver = Resolver::new(scope, schema, true);
868 resolver.is_ambiguous(column_name)
869}
870
871fn qualified_table_name(table: &TableRef) -> String {
873 let mut parts = Vec::new();
874 if let Some(catalog) = &table.catalog {
875 parts.push(catalog.name.clone());
876 }
877 if let Some(schema) = &table.schema {
878 parts.push(schema.name.clone());
879 }
880 parts.push(table.name.name.clone());
881 parts.join(".")
882}
883
884#[cfg(test)]
885mod tests {
886 use super::*;
887 use crate::dialects::Dialect;
888 use crate::expressions::DataType;
889 use crate::parser::Parser;
890 use crate::schema::MappingSchema;
891 use crate::scope::build_scope;
892
893 fn create_test_schema() -> MappingSchema {
894 let mut schema = MappingSchema::new();
895 schema
897 .add_table(
898 "users",
899 &[
900 (
901 "id".to_string(),
902 DataType::Int {
903 length: None,
904 integer_spelling: false,
905 },
906 ),
907 ("name".to_string(), DataType::Text),
908 ("email".to_string(), DataType::Text),
909 ],
910 None,
911 )
912 .unwrap();
913 schema
914 .add_table(
915 "orders",
916 &[
917 (
918 "id".to_string(),
919 DataType::Int {
920 length: None,
921 integer_spelling: false,
922 },
923 ),
924 (
925 "user_id".to_string(),
926 DataType::Int {
927 length: None,
928 integer_spelling: false,
929 },
930 ),
931 (
932 "amount".to_string(),
933 DataType::Double {
934 precision: None,
935 scale: None,
936 },
937 ),
938 ],
939 None,
940 )
941 .unwrap();
942 schema
943 }
944
945 #[test]
946 fn test_resolver_basic() {
947 let ast = Parser::parse_sql("SELECT id, name FROM users").expect("Failed to parse");
948 let scope = build_scope(&ast[0]);
949 let schema = create_test_schema();
950 let mut resolver = Resolver::new(&scope, &schema, true);
951
952 let table = resolver.get_table("name");
954 assert_eq!(table, Some("users".to_string()));
955 }
956
957 #[test]
958 fn test_resolver_ambiguous_column() {
959 let ast =
960 Parser::parse_sql("SELECT id FROM users JOIN orders ON users.id = orders.user_id")
961 .expect("Failed to parse");
962 let scope = build_scope(&ast[0]);
963 let schema = create_test_schema();
964 let mut resolver = Resolver::new(&scope, &schema, true);
965
966 assert!(resolver.is_ambiguous("id"));
968
969 assert!(!resolver.is_ambiguous("name"));
971
972 assert!(!resolver.is_ambiguous("amount"));
974 }
975
976 #[test]
977 fn test_resolver_unambiguous_column() {
978 let ast = Parser::parse_sql(
979 "SELECT name, amount FROM users JOIN orders ON users.id = orders.user_id",
980 )
981 .expect("Failed to parse");
982 let scope = build_scope(&ast[0]);
983 let schema = create_test_schema();
984 let mut resolver = Resolver::new(&scope, &schema, true);
985
986 let table = resolver.get_table("name");
988 assert_eq!(table, Some("users".to_string()));
989
990 let table = resolver.get_table("amount");
992 assert_eq!(table, Some("orders".to_string()));
993 }
994
995 #[test]
996 fn test_resolver_with_alias() {
997 let ast = Parser::parse_sql("SELECT u.id FROM users AS u").expect("Failed to parse");
998 let scope = build_scope(&ast[0]);
999 let schema = create_test_schema();
1000 let _resolver = Resolver::new(&scope, &schema, true);
1001
1002 assert!(scope.sources.contains_key("u"));
1004 }
1005
1006 #[test]
1007 fn test_sources_for_column() {
1008 let ast = Parser::parse_sql("SELECT * FROM users JOIN orders ON users.id = orders.user_id")
1009 .expect("Failed to parse");
1010 let scope = build_scope(&ast[0]);
1011 let schema = create_test_schema();
1012 let mut resolver = Resolver::new(&scope, &schema, true);
1013
1014 let sources = resolver.sources_for_column("id");
1016 assert!(sources.contains(&"users".to_string()));
1017 assert!(sources.contains(&"orders".to_string()));
1018
1019 let sources = resolver.sources_for_column("email");
1021 assert_eq!(sources, vec!["users".to_string()]);
1022 }
1023
1024 #[test]
1025 fn test_all_columns() {
1026 let ast = Parser::parse_sql("SELECT * FROM users").expect("Failed to parse");
1027 let scope = build_scope(&ast[0]);
1028 let schema = create_test_schema();
1029 let mut resolver = Resolver::new(&scope, &schema, true);
1030
1031 let all = resolver.all_columns();
1032 assert!(all.contains("id"));
1033 assert!(all.contains("name"));
1034 assert!(all.contains("email"));
1035 }
1036
1037 #[test]
1038 fn test_resolver_cte_projected_alias_column() {
1039 let ast = Parser::parse_sql(
1040 "WITH my_cte AS (SELECT id AS emp_id FROM users) SELECT emp_id FROM my_cte",
1041 )
1042 .expect("Failed to parse");
1043 let scope = build_scope(&ast[0]);
1044 let schema = create_test_schema();
1045 let mut resolver = Resolver::new(&scope, &schema, true);
1046
1047 let table = resolver.get_table("emp_id");
1048 assert_eq!(table, Some("my_cte".to_string()));
1049 }
1050
1051 #[test]
1052 fn test_resolver_uses_name_aligned_set_operation_outputs() {
1053 for dialect_type in [DialectType::DuckDB, DialectType::Snowflake] {
1054 let expr = Dialect::get(dialect_type)
1055 .parse(
1056 "SELECT combined.right_value FROM (\
1057 SELECT 1 AS left_value UNION ALL BY NAME \
1058 SELECT 2 AS right_value\
1059 ) AS combined",
1060 )
1061 .expect("parse")
1062 .remove(0);
1063 let scope = build_scope(&expr);
1064 let schema = MappingSchema::with_dialect(dialect_type);
1065 let mut resolver = Resolver::new(&scope, &schema, true);
1066
1067 assert_eq!(
1068 resolver.get_table("right_value"),
1069 Some("combined".to_string()),
1070 "right-only output was not exposed for {dialect_type:?}"
1071 );
1072 }
1073
1074 let dialect_type = DialectType::BigQuery;
1075 let expr = Dialect::get(dialect_type)
1076 .parse(
1077 "SELECT combined.c FROM (\
1078 SELECT 1 AS a, 2 AS b FULL OUTER UNION ALL BY NAME \
1079 SELECT 3 AS b, 4 AS c\
1080 ) AS combined",
1081 )
1082 .expect("parse")
1083 .remove(0);
1084 let scope = build_scope(&expr);
1085 let schema = MappingSchema::with_dialect(dialect_type);
1086 let mut resolver = Resolver::new(&scope, &schema, true);
1087 assert_eq!(resolver.get_table("c"), Some("combined".to_string()));
1088 }
1089
1090 #[test]
1091 fn test_resolve_column_helper() {
1092 let ast = Parser::parse_sql("SELECT name FROM users").expect("Failed to parse");
1093 let scope = build_scope(&ast[0]);
1094 let schema = create_test_schema();
1095
1096 let table = resolve_column(&scope, &schema, "name", true);
1097 assert_eq!(table, Some("users".to_string()));
1098 }
1099
1100 #[test]
1101 fn test_resolver_bigquery_mixed_case_column_names() {
1102 let dialect = Dialect::get(DialectType::BigQuery);
1103 let expr = dialect
1104 .parse("SELECT Name AS name FROM teams")
1105 .unwrap()
1106 .into_iter()
1107 .next()
1108 .expect("expected one expression");
1109 let scope = build_scope(&expr);
1110
1111 let mut schema = MappingSchema::with_dialect(DialectType::BigQuery);
1112 schema
1113 .add_table(
1114 "teams",
1115 &[("Name".into(), DataType::String { length: None })],
1116 None,
1117 )
1118 .expect("schema setup");
1119
1120 let mut resolver = Resolver::new(&scope, &schema, true);
1121 let table = resolver.get_table("Name");
1122 assert_eq!(table, Some("teams".to_string()));
1123
1124 let table = resolver.get_table("name");
1125 assert_eq!(table, Some("teams".to_string()));
1126 }
1127}