1use crate::dialects::transform_recursive;
9use crate::dialects::DialectType;
10use crate::expressions::{
11 Alias, BinaryOp, Column, DotAccess, Expression, Identifier, Join, JoinKind, LateralView,
12 Literal, Over, Paren, Select, TableRef, VarArgFunc, With,
13};
14use crate::resolver::{Resolver, ResolverError};
15use crate::schema::{normalize_name, Schema};
16use crate::scope::{build_scope, traverse_scope, Scope};
17use std::cell::RefCell;
18use std::collections::{HashMap, HashSet};
19use thiserror::Error;
20
21#[derive(Debug, Error, Clone)]
23pub enum QualifyColumnsError {
24 #[error("Unknown table: {0}")]
25 UnknownTable(String),
26
27 #[error("Unknown column: {0}")]
28 UnknownColumn(String),
29
30 #[error("Ambiguous column: {0}")]
31 AmbiguousColumn(String),
32
33 #[error("Cannot automatically join: {0}")]
34 CannotAutoJoin(String),
35
36 #[error("Unknown output column: {0}")]
37 UnknownOutputColumn(String),
38
39 #[error("Column could not be resolved: {column}{for_table}")]
40 ColumnNotResolved { column: String, for_table: String },
41
42 #[error("Resolver error: {0}")]
43 ResolverError(#[from] ResolverError),
44}
45
46pub type QualifyColumnsResult<T> = Result<T, QualifyColumnsError>;
48
49#[derive(Debug, Clone, Default)]
51pub struct QualifyColumnsOptions {
52 pub expand_alias_refs: bool,
54 pub expand_stars: bool,
56 pub infer_schema: Option<bool>,
58 pub allow_partial_qualification: bool,
60 pub dialect: Option<DialectType>,
62}
63
64impl QualifyColumnsOptions {
65 pub fn new() -> Self {
67 Self {
68 expand_alias_refs: true,
69 expand_stars: true,
70 infer_schema: None,
71 allow_partial_qualification: false,
72 dialect: None,
73 }
74 }
75
76 pub fn with_expand_alias_refs(mut self, expand: bool) -> Self {
78 self.expand_alias_refs = expand;
79 self
80 }
81
82 pub fn with_expand_stars(mut self, expand: bool) -> Self {
84 self.expand_stars = expand;
85 self
86 }
87
88 pub fn with_dialect(mut self, dialect: DialectType) -> Self {
90 self.dialect = Some(dialect);
91 self
92 }
93
94 pub fn with_allow_partial(mut self, allow: bool) -> Self {
96 self.allow_partial_qualification = allow;
97 self
98 }
99}
100
101pub fn qualify_columns(
116 expression: Expression,
117 schema: &dyn Schema,
118 options: &QualifyColumnsOptions,
119) -> QualifyColumnsResult<Expression> {
120 let infer_schema = options.infer_schema.unwrap_or(schema.is_empty());
121 let dialect = options.dialect.or_else(|| schema.dialect());
122 let first_error: RefCell<Option<QualifyColumnsError>> = RefCell::new(None);
123
124 let transformed = transform_recursive(expression, &|node| {
125 if first_error.borrow().is_some() {
126 return Ok(node);
127 }
128
129 match node {
130 Expression::Select(mut select) => {
131 if let Some(with) = &mut select.with {
132 pushdown_cte_alias_columns_with(with);
133 }
134
135 let scope_expr = Expression::Select(select.clone());
136 let scope = build_scope(&scope_expr);
137 let mut resolver = Resolver::new(&scope, schema, infer_schema);
138
139 let column_tables = if first_error.borrow().is_none() {
141 match expand_using(&mut select, &scope, &mut resolver) {
142 Ok(ct) => ct,
143 Err(err) => {
144 *first_error.borrow_mut() = Some(err);
145 HashMap::new()
146 }
147 }
148 } else {
149 HashMap::new()
150 };
151
152 if first_error.borrow().is_none() && options.expand_alias_refs {
155 if let Err(err) = expand_alias_refs(&mut select, &mut resolver, dialect) {
156 *first_error.borrow_mut() = Some(err);
157 }
158 }
159
160 if first_error.borrow().is_none() {
164 if let Err(err) =
165 normalize_dotted_columns_in_scope(&mut select, &scope, &mut resolver)
166 {
167 *first_error.borrow_mut() = Some(err);
168 }
169 }
170
171 if first_error.borrow().is_none() {
173 if let Err(err) = qualify_columns_in_scope(
174 &mut select,
175 &scope,
176 &mut resolver,
177 options.allow_partial_qualification,
178 ) {
179 *first_error.borrow_mut() = Some(err);
180 }
181 }
182
183 if first_error.borrow().is_none() && options.expand_stars {
185 if let Err(err) =
186 expand_stars(&mut select, &scope, &mut resolver, &column_tables)
187 {
188 *first_error.borrow_mut() = Some(err);
189 }
190 }
191
192 if first_error.borrow().is_none() {
194 if let Err(err) = qualify_outputs_select(&mut select) {
195 *first_error.borrow_mut() = Some(err);
196 }
197 }
198
199 if first_error.borrow().is_none() {
201 if let Err(err) = expand_group_by(&mut select, dialect) {
202 *first_error.borrow_mut() = Some(err);
203 }
204 }
205
206 Ok(Expression::Select(select))
207 }
208 _ => Ok(node),
209 }
210 })
211 .map_err(|err| QualifyColumnsError::CannotAutoJoin(err.to_string()))?;
212
213 if let Some(err) = first_error.into_inner() {
214 return Err(err);
215 }
216
217 Ok(transformed)
218}
219
220pub fn validate_qualify_columns(expression: &Expression) -> QualifyColumnsResult<()> {
225 let mut all_unqualified = Vec::new();
226
227 for scope in traverse_scope(expression) {
228 if let Expression::Select(_) = &scope.expression {
229 let unqualified = get_unqualified_columns(&scope);
231
232 let external = get_external_columns(&scope);
234 if !external.is_empty() && !is_correlated_subquery(&scope) {
235 let first = &external[0];
236 let for_table = if first.table.is_some() {
237 format!(" for table: '{}'", first.table.as_ref().unwrap())
238 } else {
239 String::new()
240 };
241 return Err(QualifyColumnsError::ColumnNotResolved {
242 column: first.name.clone(),
243 for_table,
244 });
245 }
246
247 all_unqualified.extend(unqualified);
248 }
249 }
250
251 if !all_unqualified.is_empty() {
252 let first = &all_unqualified[0];
253 return Err(QualifyColumnsError::AmbiguousColumn(first.name.clone()));
254 }
255
256 Ok(())
257}
258
259fn get_source_name(expr: &Expression) -> Option<String> {
261 match expr {
262 Expression::Table(t) => Some(
263 t.alias
264 .as_ref()
265 .map(|a| a.name.clone())
266 .unwrap_or_else(|| t.name.name.clone()),
267 ),
268 Expression::Subquery(sq) => sq.alias.as_ref().map(|a| a.name.clone()),
269 Expression::Pivot(pivot) => Some(pivot_source_name(
270 &pivot.this,
271 pivot.alias.as_ref().map(|alias| alias.name.as_str()),
272 )),
273 Expression::Unpivot(unpivot) => Some(pivot_source_name(
274 &unpivot.this,
275 unpivot.alias.as_ref().map(|alias| alias.name.as_str()),
276 )),
277 _ => None,
278 }
279}
280
281fn pivot_source_name(source: &Expression, explicit_alias: Option<&str>) -> String {
282 if let Some(alias) = explicit_alias {
283 return alias.to_string();
284 }
285
286 match source {
287 Expression::Table(table) => table
288 .alias
289 .as_ref()
290 .map(|alias| alias.name.clone())
291 .unwrap_or_else(|| table.name.name.clone()),
292 Expression::Subquery(subquery) => subquery
293 .alias
294 .as_ref()
295 .map(|alias| alias.name.clone())
296 .unwrap_or_else(|| "_0".to_string()),
297 Expression::Paren(paren) => pivot_source_name(&paren.this, explicit_alias),
298 _ => "_0".to_string(),
299 }
300}
301
302fn get_ordered_source_names(select: &Select) -> Vec<String> {
305 let mut ordered = Vec::new();
306 if let Some(from) = &select.from {
307 for expr in &from.expressions {
308 if let Some(name) = get_source_name(expr) {
309 ordered.push(name);
310 }
311 }
312 }
313 for join in &select.joins {
314 if is_semi_or_anti_join_kind(join.kind) {
315 continue;
316 }
317 if let Some(name) = get_source_name(&join.this) {
318 ordered.push(name);
319 }
320 }
321 ordered
322}
323
324fn is_semi_or_anti_join_kind(kind: JoinKind) -> bool {
325 matches!(
326 kind,
327 JoinKind::Semi
328 | JoinKind::Anti
329 | JoinKind::LeftSemi
330 | JoinKind::LeftAnti
331 | JoinKind::RightSemi
332 | JoinKind::RightAnti
333 )
334}
335
336fn make_coalesce(column_name: &str, tables: &[String]) -> Expression {
338 let args: Vec<Expression> = tables
339 .iter()
340 .map(|t| Expression::qualified_column(t.as_str(), column_name))
341 .collect();
342 Expression::Coalesce(Box::new(VarArgFunc {
343 expressions: args,
344 original_name: None,
345 inferred_type: None,
346 }))
347}
348
349fn expand_using(
355 select: &mut Select,
356 _scope: &Scope,
357 resolver: &mut Resolver,
358) -> QualifyColumnsResult<HashMap<String, Vec<String>>> {
359 let mut columns: HashMap<String, String> = HashMap::new();
362 let mut column_order: Vec<String> = Vec::new();
365
366 let mut column_tables: HashMap<String, Vec<String>> = HashMap::new();
369
370 let join_names: HashSet<String> = select
372 .joins
373 .iter()
374 .filter_map(|j| get_source_name(&j.this))
375 .collect();
376
377 let all_ordered = get_ordered_source_names(select);
378 let mut ordered: Vec<String> = all_ordered
379 .iter()
380 .filter(|name| !join_names.contains(name.as_str()))
381 .cloned()
382 .collect();
383 let mut accumulated_schema_known = true;
384
385 if join_names.is_empty() {
386 return Ok(column_tables);
387 }
388
389 fn update_source_columns(
391 source_name: &str,
392 columns: &mut HashMap<String, String>,
393 column_order: &mut Vec<String>,
394 resolver: &mut Resolver,
395 ) -> bool {
396 let Ok(source_cols) = resolver.get_source_columns(source_name) else {
397 return false;
398 };
399 let schema_known = !source_cols.is_empty()
400 && !source_cols
401 .iter()
402 .any(|column| column == "*" || column.is_empty());
403 for col_name in source_cols {
404 let normalized = normalize_column_name(&col_name, resolver.dialect);
405 if let std::collections::hash_map::Entry::Vacant(entry) = columns.entry(normalized) {
406 entry.insert(source_name.to_string());
407 column_order.push(col_name);
408 }
409 }
410 schema_known
411 }
412
413 for source_name in &ordered {
415 accumulated_schema_known &=
416 update_source_columns(source_name, &mut columns, &mut column_order, resolver);
417 }
418
419 for i in 0..select.joins.len() {
420 let source_table = ordered.last().cloned().unwrap_or_default();
422 if !source_table.is_empty() {
423 accumulated_schema_known &=
424 update_source_columns(&source_table, &mut columns, &mut column_order, resolver);
425 }
426
427 let join_table = get_source_name(&select.joins[i].this).unwrap_or_default();
429 ordered.push(join_table.clone());
430
431 let join_columns: Vec<String> =
432 resolver.get_source_columns(&join_table).unwrap_or_default();
433 let star = normalize_column_name("*", resolver.dialect);
434 let right_schema_known = !join_columns.is_empty()
435 && !join_columns
436 .iter()
437 .any(|column| normalize_column_name(column, resolver.dialect) == star);
438
439 let join_kind = select.joins[i].kind;
444 if select.joins[i].using.is_empty()
445 && matches!(
446 join_kind,
447 JoinKind::Natural
448 | JoinKind::NaturalLeft
449 | JoinKind::NaturalRight
450 | JoinKind::NaturalFull
451 )
452 {
453 let left_schema_known =
454 accumulated_schema_known && !columns.is_empty() && !columns.contains_key(&star);
455
456 if left_schema_known && right_schema_known {
457 let right_columns: HashSet<String> = join_columns
458 .iter()
459 .map(|column| normalize_column_name(column, resolver.dialect))
460 .collect();
461 let implicit_using: Vec<Identifier> = column_order
462 .iter()
463 .filter(|column| {
464 right_columns.contains(&normalize_column_name(column, resolver.dialect))
465 })
466 .map(|column| Identifier::new(column))
467 .collect();
468
469 if !implicit_using.is_empty() {
470 select.joins[i].using = implicit_using;
471 select.joins[i].kind = match join_kind {
472 JoinKind::Natural => JoinKind::Inner,
473 JoinKind::NaturalLeft => JoinKind::Left,
474 JoinKind::NaturalRight => JoinKind::Right,
475 JoinKind::NaturalFull => JoinKind::Full,
476 _ => unreachable!("checked NATURAL join kind above"),
477 };
478 }
479 }
480 }
481
482 if select.joins[i].using.is_empty() {
485 accumulated_schema_known &= right_schema_known;
486 continue;
487 }
488
489 let using_identifiers: Vec<String> = select.joins[i]
490 .using
491 .iter()
492 .map(|id| id.name.clone())
493 .collect();
494
495 let using_count = using_identifiers.len();
496 let is_semi_or_anti = matches!(
497 select.joins[i].kind,
498 crate::expressions::JoinKind::Semi
499 | crate::expressions::JoinKind::Anti
500 | crate::expressions::JoinKind::LeftSemi
501 | crate::expressions::JoinKind::LeftAnti
502 | crate::expressions::JoinKind::RightSemi
503 | crate::expressions::JoinKind::RightAnti
504 );
505
506 let mut conditions: Vec<Expression> = Vec::new();
507
508 for identifier in &using_identifiers {
509 let normalized_identifier = normalize_column_name(identifier, resolver.dialect);
510 let table = columns
511 .get(&normalized_identifier)
512 .cloned()
513 .unwrap_or_else(|| source_table.clone());
514
515 let lhs = if i == 0 || using_count == 1 {
517 Expression::qualified_column(table.as_str(), identifier.as_str())
519 } else {
520 let coalesce_cols: Vec<String> = ordered[..ordered.len() - 1]
523 .iter()
524 .filter(|t| {
525 resolver
526 .get_source_columns(t)
527 .unwrap_or_default()
528 .iter()
529 .any(|column| {
530 normalize_column_name(column, resolver.dialect)
531 == normalized_identifier
532 })
533 })
534 .cloned()
535 .collect();
536
537 if coalesce_cols.len() > 1 {
538 make_coalesce(identifier, &coalesce_cols)
539 } else {
540 Expression::qualified_column(table.as_str(), identifier.as_str())
541 }
542 };
543
544 let rhs = Expression::qualified_column(join_table.as_str(), identifier.as_str());
546
547 conditions.push(Expression::Eq(Box::new(BinaryOp::new(lhs, rhs))));
548
549 if !is_semi_or_anti {
551 let tables = column_tables
552 .entry(normalized_identifier)
553 .or_insert_with(Vec::new);
554 if !tables.contains(&table) {
555 tables.push(table.clone());
556 }
557 if !tables.contains(&join_table) {
558 tables.push(join_table.clone());
559 }
560 }
561 }
562
563 let on_condition = conditions
565 .into_iter()
566 .reduce(|acc, cond| Expression::And(Box::new(BinaryOp::new(acc, cond))))
567 .expect("at least one USING column");
568
569 select.joins[i].on = Some(on_condition);
571 select.joins[i].using = vec![];
572 accumulated_schema_known &= right_schema_known;
573 }
574
575 if !column_tables.is_empty() {
577 let mut new_expressions = Vec::with_capacity(select.expressions.len());
579 for expr in &select.expressions {
580 match expr {
581 Expression::Column(col) if col.table.is_none() => {
582 let normalized = normalize_column_name(&col.name.name, resolver.dialect);
583 let Some(tables) = column_tables.get(&normalized) else {
584 new_expressions.push(expr.clone());
585 continue;
586 };
587 let coalesce = make_coalesce(&col.name.name, tables);
588 new_expressions.push(Expression::Alias(Box::new(Alias {
590 this: coalesce,
591 alias: col.name.clone(),
592 column_aliases: vec![],
593 alias_explicit_as: false,
594 alias_keyword: None,
595 pre_alias_comments: vec![],
596 trailing_comments: vec![],
597 inferred_type: None,
598 })));
599 }
600 _ => {
601 let mut rewritten = expr.clone();
602 rewrite_using_columns_in_expression(
603 &mut rewritten,
604 &column_tables,
605 resolver.dialect,
606 );
607 new_expressions.push(rewritten);
608 }
609 }
610 }
611 select.expressions = new_expressions;
612
613 if let Some(where_clause) = &mut select.where_clause {
615 rewrite_using_columns_in_expression(
616 &mut where_clause.this,
617 &column_tables,
618 resolver.dialect,
619 );
620 }
621
622 if let Some(group_by) = &mut select.group_by {
624 for expr in &mut group_by.expressions {
625 rewrite_using_columns_in_expression(expr, &column_tables, resolver.dialect);
626 }
627 }
628
629 if let Some(having) = &mut select.having {
631 rewrite_using_columns_in_expression(&mut having.this, &column_tables, resolver.dialect);
632 }
633
634 if let Some(qualify) = &mut select.qualify {
636 rewrite_using_columns_in_expression(
637 &mut qualify.this,
638 &column_tables,
639 resolver.dialect,
640 );
641 }
642
643 if let Some(order_by) = &mut select.order_by {
645 for ordered in &mut order_by.expressions {
646 rewrite_using_columns_in_expression(
647 &mut ordered.this,
648 &column_tables,
649 resolver.dialect,
650 );
651 }
652 }
653 }
654
655 Ok(column_tables)
656}
657
658fn rewrite_using_columns_in_expression(
660 expr: &mut Expression,
661 column_tables: &HashMap<String, Vec<String>>,
662 dialect: Option<DialectType>,
663) {
664 let transformed = transform_recursive(expr.clone(), &|node| match node {
665 Expression::Column(col) if col.table.is_none() => {
666 let normalized = normalize_column_name(&col.name.name, dialect);
667 if let Some(tables) = column_tables.get(&normalized) {
668 Ok(make_coalesce(&col.name.name, tables))
669 } else {
670 Ok(Expression::Column(col))
671 }
672 }
673 other => Ok(other),
674 });
675
676 if let Ok(next) = transformed {
677 *expr = next;
678 }
679}
680
681pub(crate) fn normalize_dotted_columns_in_scope(
689 select: &mut Select,
690 scope: &Scope,
691 resolver: &mut Resolver,
692) -> QualifyColumnsResult<()> {
693 for expression in &mut select.expressions {
694 normalize_dotted_columns_in_expression(expression, scope, resolver)?;
695 }
696 if let Some(where_clause) = &mut select.where_clause {
697 normalize_dotted_columns_in_expression(&mut where_clause.this, scope, resolver)?;
698 }
699 if let Some(group_by) = &mut select.group_by {
700 for expression in &mut group_by.expressions {
701 normalize_dotted_columns_in_expression(expression, scope, resolver)?;
702 }
703 }
704 if let Some(having) = &mut select.having {
705 normalize_dotted_columns_in_expression(&mut having.this, scope, resolver)?;
706 }
707 if let Some(qualify) = &mut select.qualify {
708 normalize_dotted_columns_in_expression(&mut qualify.this, scope, resolver)?;
709 }
710 if let Some(order_by) = &mut select.order_by {
711 for ordered in &mut order_by.expressions {
712 normalize_dotted_columns_in_expression(&mut ordered.this, scope, resolver)?;
713 }
714 }
715 for join in &mut select.joins {
716 normalize_dotted_columns_in_expression(&mut join.this, scope, resolver)?;
717 if let Some(on) = &mut join.on {
718 normalize_dotted_columns_in_expression(on, scope, resolver)?;
719 }
720 }
721 Ok(())
722}
723
724pub(crate) fn normalize_dotted_columns_in_expression(
725 expression: &mut Expression,
726 scope: &Scope,
727 resolver: &mut Resolver,
728) -> QualifyColumnsResult<()> {
729 let resolver = RefCell::new(resolver);
730 let transformed = transform_recursive(expression.clone(), &|node| {
731 let Expression::Column(column) = node else {
732 return Ok(node);
733 };
734 let Some(root) = column.table.as_ref() else {
735 return Ok(Expression::Column(column));
736 };
737
738 let root_is_source = scope
739 .sources
740 .keys()
741 .any(|source| source.eq_ignore_ascii_case(&root.name));
742 if root_is_source {
743 return Ok(Expression::Column(column));
744 }
745
746 let Some(source_name) = resolver.borrow_mut().get_table(&root.name) else {
747 return Ok(Expression::Column(column));
748 };
749
750 let root_column = Expression::boxed_column(Column {
751 name: root.clone(),
752 table: Some(Identifier::new(source_name)),
753 join_mark: column.join_mark,
754 trailing_comments: column.trailing_comments.clone(),
755 span: column.span,
756 inferred_type: None,
757 });
758
759 Ok(Expression::Dot(Box::new(DotAccess {
760 this: root_column,
761 field: column.name.clone(),
762 inferred_type: None,
763 })))
764 })
765 .map_err(|error| QualifyColumnsError::CannotAutoJoin(error.to_string()))?;
766
767 *expression = transformed;
768 Ok(())
769}
770
771fn qualify_columns_in_scope(
773 select: &mut Select,
774 scope: &Scope,
775 resolver: &mut Resolver,
776 allow_partial: bool,
777) -> QualifyColumnsResult<()> {
778 for expr in &mut select.expressions {
779 qualify_columns_in_expression(expr, scope, resolver, allow_partial)?;
780 }
781 if let Some(where_clause) = &mut select.where_clause {
782 qualify_columns_in_expression(&mut where_clause.this, scope, resolver, allow_partial)?;
783 }
784 if let Some(group_by) = &mut select.group_by {
785 for expr in &mut group_by.expressions {
786 qualify_columns_in_expression(expr, scope, resolver, allow_partial)?;
787 }
788 }
789 if let Some(having) = &mut select.having {
790 qualify_columns_in_expression(&mut having.this, scope, resolver, allow_partial)?;
791 }
792 if let Some(qualify) = &mut select.qualify {
793 qualify_columns_in_expression(&mut qualify.this, scope, resolver, allow_partial)?;
794 }
795 if let Some(order_by) = &mut select.order_by {
796 for ordered in &mut order_by.expressions {
797 qualify_columns_in_expression(&mut ordered.this, scope, resolver, allow_partial)?;
798 }
799 }
800 for join in &mut select.joins {
801 qualify_columns_in_expression(&mut join.this, scope, resolver, allow_partial)?;
802 if let Some(on) = &mut join.on {
803 qualify_columns_in_expression(on, scope, resolver, allow_partial)?;
804 }
805 }
806 Ok(())
807}
808
809fn expand_alias_refs(
816 select: &mut Select,
817 _resolver: &mut Resolver,
818 _dialect: Option<DialectType>,
819) -> QualifyColumnsResult<()> {
820 let mut alias_to_expression: HashMap<String, (Expression, usize)> = HashMap::new();
821
822 for (i, expr) in select.expressions.iter_mut().enumerate() {
823 replace_alias_refs_in_expression(expr, &alias_to_expression, false);
824 if let Expression::Alias(alias) = expr {
825 alias_to_expression.insert(alias.alias.name.clone(), (alias.this.clone(), i + 1));
826 }
827 }
828
829 if let Some(where_clause) = &mut select.where_clause {
830 replace_alias_refs_in_expression(&mut where_clause.this, &alias_to_expression, false);
831 }
832 if let Some(group_by) = &mut select.group_by {
833 for expr in &mut group_by.expressions {
834 replace_alias_refs_in_expression(expr, &alias_to_expression, true);
835 }
836 }
837 if let Some(having) = &mut select.having {
838 replace_alias_refs_in_expression(&mut having.this, &alias_to_expression, false);
839 }
840 if let Some(qualify) = &mut select.qualify {
841 replace_alias_refs_in_expression(&mut qualify.this, &alias_to_expression, false);
842 }
843 if let Some(order_by) = &mut select.order_by {
844 for ordered in &mut order_by.expressions {
845 replace_alias_refs_in_expression(&mut ordered.this, &alias_to_expression, false);
846 }
847 }
848
849 Ok(())
850}
851
852fn expand_group_by(select: &mut Select, _dialect: Option<DialectType>) -> QualifyColumnsResult<()> {
859 let projections = select.expressions.clone();
860
861 if let Some(group_by) = &mut select.group_by {
862 for group_expr in &mut group_by.expressions {
863 if let Some(index) = positional_reference(group_expr) {
864 let replacement = select_expression_at_position(&projections, index)?;
865 *group_expr = replacement;
866 }
867 }
868 }
869 Ok(())
870}
871
872fn expand_stars(
882 select: &mut Select,
883 _scope: &Scope,
884 resolver: &mut Resolver,
885 column_tables: &HashMap<String, Vec<String>>,
886) -> QualifyColumnsResult<()> {
887 let mut new_selections: Vec<Expression> = Vec::new();
888 let mut has_star = false;
889 let mut coalesced_columns: HashSet<String> = HashSet::new();
890
891 let ordered_sources = get_ordered_source_names(select);
893
894 for expr in &select.expressions {
895 match expr {
896 Expression::Star(star) => {
897 has_star = true;
898 if let Some(table) = &star.table {
899 let table_name = &table.name;
900 if !ordered_sources.contains(table_name) {
901 return Err(QualifyColumnsError::UnknownTable(table_name.clone()));
902 }
903 if let Ok(columns) = resolver.get_source_columns(table_name) {
904 if columns.contains(&"*".to_string()) || columns.is_empty() {
905 return Ok(());
906 }
907 for col_name in &columns {
908 let normalized = normalize_column_name(col_name, resolver.dialect);
909 if coalesced_columns.contains(&normalized) {
910 continue;
911 }
912 if let Some(tables) = column_tables.get(&normalized) {
913 if tables.contains(table_name) {
914 coalesced_columns.insert(normalized);
915 let coalesce = make_coalesce(col_name, tables);
916 new_selections.push(Expression::Alias(Box::new(Alias {
917 this: coalesce,
918 alias: Identifier::new(col_name),
919 column_aliases: vec![],
920 alias_explicit_as: false,
921 alias_keyword: None,
922 pre_alias_comments: vec![],
923 trailing_comments: vec![],
924 inferred_type: None,
925 })));
926 continue;
927 }
928 }
929 new_selections
930 .push(create_qualified_column(col_name, Some(table_name)));
931 }
932 }
933 } else {
934 for source_name in &ordered_sources {
935 if let Ok(columns) = resolver.get_source_columns(source_name) {
936 if columns.contains(&"*".to_string()) || columns.is_empty() {
937 return Ok(());
938 }
939 for col_name in &columns {
940 let normalized = normalize_column_name(col_name, resolver.dialect);
941 if coalesced_columns.contains(&normalized) {
942 continue;
944 }
945 if let Some(tables) = column_tables.get(&normalized) {
946 if tables.contains(source_name) {
947 coalesced_columns.insert(normalized);
949 let coalesce = make_coalesce(col_name, tables);
950 new_selections.push(Expression::Alias(Box::new(Alias {
951 this: coalesce,
952 alias: Identifier::new(col_name),
953 column_aliases: vec![],
954 alias_explicit_as: false,
955 alias_keyword: None,
956 pre_alias_comments: vec![],
957 trailing_comments: vec![],
958 inferred_type: None,
959 })));
960 continue;
961 }
962 }
963 new_selections
964 .push(create_qualified_column(col_name, Some(source_name)));
965 }
966 }
967 }
968 }
969 }
970 Expression::Column(col) if is_star_column(col) => {
971 has_star = true;
972 if let Some(table) = &col.table {
973 let table_name = &table.name;
974 if !ordered_sources.contains(table_name) {
975 return Err(QualifyColumnsError::UnknownTable(table_name.clone()));
976 }
977 if let Ok(columns) = resolver.get_source_columns(table_name) {
978 if columns.contains(&"*".to_string()) || columns.is_empty() {
979 return Ok(());
980 }
981 for col_name in &columns {
982 let normalized = normalize_column_name(col_name, resolver.dialect);
983 if coalesced_columns.contains(&normalized) {
984 continue;
985 }
986 if let Some(tables) = column_tables.get(&normalized) {
987 if tables.contains(table_name) {
988 coalesced_columns.insert(normalized);
989 let coalesce = make_coalesce(col_name, tables);
990 new_selections.push(Expression::Alias(Box::new(Alias {
991 this: coalesce,
992 alias: Identifier::new(col_name),
993 column_aliases: vec![],
994 alias_explicit_as: false,
995 alias_keyword: None,
996 pre_alias_comments: vec![],
997 trailing_comments: vec![],
998 inferred_type: None,
999 })));
1000 continue;
1001 }
1002 }
1003 new_selections
1004 .push(create_qualified_column(col_name, Some(table_name)));
1005 }
1006 }
1007 }
1008 }
1009 _ => new_selections.push(expr.clone()),
1010 }
1011 }
1012
1013 if has_star {
1014 select.expressions = new_selections;
1015 }
1016
1017 Ok(())
1018}
1019
1020pub fn qualify_outputs(scope: &Scope) -> QualifyColumnsResult<()> {
1027 if let Expression::Select(mut select) = scope.expression.clone() {
1028 qualify_outputs_select(&mut select)?;
1029 }
1030 Ok(())
1031}
1032
1033fn qualify_outputs_select(select: &mut Select) -> QualifyColumnsResult<()> {
1034 let mut new_selections: Vec<Expression> = Vec::new();
1035
1036 for (i, expr) in select.expressions.iter().enumerate() {
1037 match expr {
1038 Expression::Alias(_) => new_selections.push(expr.clone()),
1039 Expression::Column(col) => {
1040 new_selections.push(create_alias(expr.clone(), &col.name.name));
1041 }
1042 Expression::Star(_) => new_selections.push(expr.clone()),
1043 _ => {
1044 let alias_name = get_output_name(expr).unwrap_or_else(|| format!("_col_{}", i));
1045 new_selections.push(create_alias(expr.clone(), &alias_name));
1046 }
1047 }
1048 }
1049
1050 select.expressions = new_selections;
1051 Ok(())
1052}
1053
1054fn qualify_columns_in_expression(
1055 expr: &mut Expression,
1056 scope: &Scope,
1057 resolver: &mut Resolver,
1058 allow_partial: bool,
1059) -> QualifyColumnsResult<()> {
1060 let first_error: RefCell<Option<QualifyColumnsError>> = RefCell::new(None);
1061 let resolver_cell: RefCell<&mut Resolver> = RefCell::new(resolver);
1062
1063 let transformed = transform_recursive(expr.clone(), &|node| {
1064 if first_error.borrow().is_some() {
1065 return Ok(node);
1066 }
1067
1068 match node {
1069 Expression::Column(mut col) => {
1070 if let Err(err) = qualify_single_column(
1071 &mut col,
1072 scope,
1073 &mut resolver_cell.borrow_mut(),
1074 allow_partial,
1075 ) {
1076 *first_error.borrow_mut() = Some(err);
1077 }
1078 Ok(Expression::Column(col))
1079 }
1080 _ => Ok(node),
1081 }
1082 })
1083 .map_err(|err| QualifyColumnsError::CannotAutoJoin(err.to_string()))?;
1084
1085 if let Some(err) = first_error.into_inner() {
1086 return Err(err);
1087 }
1088
1089 *expr = transformed;
1090 Ok(())
1091}
1092
1093fn qualify_single_column(
1094 col: &mut Column,
1095 scope: &Scope,
1096 resolver: &mut Resolver,
1097 allow_partial: bool,
1098) -> QualifyColumnsResult<()> {
1099 if is_star_column(col) {
1100 return Ok(());
1101 }
1102
1103 if let Some(table) = &col.table {
1104 let table_name = &table.name;
1105 if !scope.sources.contains_key(table_name) {
1106 if resolver.table_exists_in_schema(table_name) {
1110 return Ok(());
1111 }
1112 return Err(QualifyColumnsError::UnknownTable(table_name.clone()));
1113 }
1114
1115 if let Ok(source_columns) = resolver.get_source_columns(table_name) {
1116 let normalized_column_name = normalize_column_name(&col.name.name, resolver.dialect);
1117 if !allow_partial
1118 && !source_columns.is_empty()
1119 && !source_columns.iter().any(|column| {
1120 normalize_column_name(column, resolver.dialect) == normalized_column_name
1121 })
1122 && !source_columns.contains(&"*".to_string())
1123 {
1124 return Err(QualifyColumnsError::UnknownColumn(col.name.name.clone()));
1125 }
1126 }
1127 return Ok(());
1128 }
1129
1130 if let Some(table_name) = resolver.get_table(&col.name.name) {
1131 col.table = Some(Identifier::new(table_name));
1132 return Ok(());
1133 }
1134
1135 if let Some(outer_table) = resolver.find_column_in_outer_schema_tables(&col.name.name) {
1138 col.table = Some(Identifier::new(outer_table));
1139 return Ok(());
1140 }
1141
1142 if !allow_partial {
1143 return Err(QualifyColumnsError::UnknownColumn(col.name.name.clone()));
1144 }
1145
1146 Ok(())
1147}
1148
1149fn normalize_column_name(name: &str, dialect: Option<DialectType>) -> String {
1150 normalize_name(name, dialect, false, true)
1151}
1152
1153fn replace_alias_refs_in_expression(
1154 expr: &mut Expression,
1155 alias_to_expression: &HashMap<String, (Expression, usize)>,
1156 literal_index: bool,
1157) {
1158 let transformed = transform_recursive(expr.clone(), &|node| match node {
1159 Expression::Column(col) if col.table.is_none() => {
1160 if let Some((alias_expr, index)) = alias_to_expression.get(&col.name.name) {
1161 if literal_index && matches!(alias_expr, Expression::Literal(_)) {
1162 return Ok(Expression::number(*index as i64));
1163 }
1164 return Ok(Expression::Paren(Box::new(Paren {
1165 this: alias_expr.clone(),
1166 trailing_comments: vec![],
1167 })));
1168 }
1169 Ok(Expression::Column(col))
1170 }
1171 other => Ok(other),
1172 });
1173
1174 if let Ok(next) = transformed {
1175 *expr = next;
1176 }
1177}
1178
1179fn positional_reference(expr: &Expression) -> Option<usize> {
1180 match expr {
1181 Expression::Literal(lit) if matches!(lit.as_ref(), Literal::Number(_)) => {
1182 let Literal::Number(value) = lit.as_ref() else {
1183 unreachable!()
1184 };
1185 value.parse::<usize>().ok()
1186 }
1187 _ => None,
1188 }
1189}
1190
1191fn select_expression_at_position(
1192 projections: &[Expression],
1193 index: usize,
1194) -> QualifyColumnsResult<Expression> {
1195 if index == 0 || index > projections.len() {
1196 return Err(QualifyColumnsError::UnknownOutputColumn(index.to_string()));
1197 }
1198
1199 let projection = projections[index - 1].clone();
1200 Ok(match projection {
1201 Expression::Alias(alias) => alias.this.clone(),
1202 other => other,
1203 })
1204}
1205
1206fn get_reserved_words(dialect: Option<DialectType>) -> HashSet<&'static str> {
1209 let mut words: HashSet<&'static str> = [
1211 "ADD",
1213 "ALL",
1214 "ALTER",
1215 "AND",
1216 "ANY",
1217 "AS",
1218 "ASC",
1219 "BETWEEN",
1220 "BY",
1221 "CASE",
1222 "CAST",
1223 "CHECK",
1224 "COLUMN",
1225 "CONSTRAINT",
1226 "CREATE",
1227 "CROSS",
1228 "CURRENT",
1229 "CURRENT_DATE",
1230 "CURRENT_TIME",
1231 "CURRENT_TIMESTAMP",
1232 "CURRENT_USER",
1233 "DATABASE",
1234 "DEFAULT",
1235 "DELETE",
1236 "DESC",
1237 "DISTINCT",
1238 "DROP",
1239 "ELSE",
1240 "END",
1241 "ESCAPE",
1242 "EXCEPT",
1243 "EXISTS",
1244 "FALSE",
1245 "FETCH",
1246 "FOR",
1247 "FOREIGN",
1248 "FROM",
1249 "FULL",
1250 "GRANT",
1251 "GROUP",
1252 "HAVING",
1253 "IF",
1254 "IN",
1255 "INDEX",
1256 "INNER",
1257 "INSERT",
1258 "INTERSECT",
1259 "INTO",
1260 "IS",
1261 "JOIN",
1262 "KEY",
1263 "LEFT",
1264 "LIKE",
1265 "LIMIT",
1266 "NATURAL",
1267 "NOT",
1268 "NULL",
1269 "OFFSET",
1270 "ON",
1271 "OR",
1272 "ORDER",
1273 "OUTER",
1274 "PRIMARY",
1275 "REFERENCES",
1276 "REPLACE",
1277 "RETURNING",
1278 "RIGHT",
1279 "ROLLBACK",
1280 "ROW",
1281 "ROWS",
1282 "SELECT",
1283 "SESSION_USER",
1284 "SET",
1285 "SOME",
1286 "TABLE",
1287 "THEN",
1288 "TO",
1289 "TRUE",
1290 "TRUNCATE",
1291 "UNION",
1292 "UNIQUE",
1293 "UPDATE",
1294 "USING",
1295 "VALUES",
1296 "VIEW",
1297 "WHEN",
1298 "WHERE",
1299 "WINDOW",
1300 "WITH",
1301 ]
1302 .iter()
1303 .copied()
1304 .collect();
1305
1306 match dialect {
1308 Some(DialectType::MySQL) => {
1309 words.extend(
1310 [
1311 "ANALYZE",
1312 "BOTH",
1313 "CHANGE",
1314 "CONDITION",
1315 "DATABASES",
1316 "DAY_HOUR",
1317 "DAY_MICROSECOND",
1318 "DAY_MINUTE",
1319 "DAY_SECOND",
1320 "DELAYED",
1321 "DETERMINISTIC",
1322 "DIV",
1323 "DUAL",
1324 "EACH",
1325 "ELSEIF",
1326 "ENCLOSED",
1327 "EXPLAIN",
1328 "FLOAT4",
1329 "FLOAT8",
1330 "FORCE",
1331 "HOUR_MICROSECOND",
1332 "HOUR_MINUTE",
1333 "HOUR_SECOND",
1334 "IGNORE",
1335 "INFILE",
1336 "INT1",
1337 "INT2",
1338 "INT3",
1339 "INT4",
1340 "INT8",
1341 "ITERATE",
1342 "KEYS",
1343 "KILL",
1344 "LEADING",
1345 "LEAVE",
1346 "LINES",
1347 "LOAD",
1348 "LOCK",
1349 "LONG",
1350 "LONGBLOB",
1351 "LONGTEXT",
1352 "LOOP",
1353 "LOW_PRIORITY",
1354 "MATCH",
1355 "MEDIUMBLOB",
1356 "MEDIUMINT",
1357 "MEDIUMTEXT",
1358 "MINUTE_MICROSECOND",
1359 "MINUTE_SECOND",
1360 "MOD",
1361 "MODIFIES",
1362 "NO_WRITE_TO_BINLOG",
1363 "OPTIMIZE",
1364 "OPTIONALLY",
1365 "OUT",
1366 "OUTFILE",
1367 "PURGE",
1368 "READS",
1369 "REGEXP",
1370 "RELEASE",
1371 "RENAME",
1372 "REPEAT",
1373 "REQUIRE",
1374 "RESIGNAL",
1375 "RETURN",
1376 "REVOKE",
1377 "RLIKE",
1378 "SCHEMA",
1379 "SCHEMAS",
1380 "SECOND_MICROSECOND",
1381 "SENSITIVE",
1382 "SEPARATOR",
1383 "SHOW",
1384 "SIGNAL",
1385 "SPATIAL",
1386 "SQL",
1387 "SQLEXCEPTION",
1388 "SQLSTATE",
1389 "SQLWARNING",
1390 "SQL_BIG_RESULT",
1391 "SQL_CALC_FOUND_ROWS",
1392 "SQL_SMALL_RESULT",
1393 "SSL",
1394 "STARTING",
1395 "STRAIGHT_JOIN",
1396 "TERMINATED",
1397 "TINYBLOB",
1398 "TINYINT",
1399 "TINYTEXT",
1400 "TRAILING",
1401 "TRIGGER",
1402 "UNDO",
1403 "UNLOCK",
1404 "UNSIGNED",
1405 "USAGE",
1406 "UTC_DATE",
1407 "UTC_TIME",
1408 "UTC_TIMESTAMP",
1409 "VARBINARY",
1410 "VARCHARACTER",
1411 "WHILE",
1412 "WRITE",
1413 "XOR",
1414 "YEAR_MONTH",
1415 "ZEROFILL",
1416 ]
1417 .iter()
1418 .copied(),
1419 );
1420 }
1421 Some(DialectType::PostgreSQL) | Some(DialectType::CockroachDB) => {
1422 words.extend(
1423 [
1424 "ANALYSE",
1425 "ANALYZE",
1426 "ARRAY",
1427 "AUTHORIZATION",
1428 "BINARY",
1429 "BOTH",
1430 "COLLATE",
1431 "CONCURRENTLY",
1432 "DO",
1433 "FREEZE",
1434 "ILIKE",
1435 "INITIALLY",
1436 "ISNULL",
1437 "LATERAL",
1438 "LEADING",
1439 "LOCALTIME",
1440 "LOCALTIMESTAMP",
1441 "NOTNULL",
1442 "ONLY",
1443 "OVERLAPS",
1444 "PLACING",
1445 "SIMILAR",
1446 "SYMMETRIC",
1447 "TABLESAMPLE",
1448 "TRAILING",
1449 "VARIADIC",
1450 "VERBOSE",
1451 ]
1452 .iter()
1453 .copied(),
1454 );
1455 }
1456 Some(DialectType::BigQuery) => {
1457 words.extend(
1458 [
1459 "ASSERT_ROWS_MODIFIED",
1460 "COLLATE",
1461 "CONTAINS",
1462 "CUBE",
1463 "DEFINE",
1464 "ENUM",
1465 "EXTRACT",
1466 "FOLLOWING",
1467 "GROUPING",
1468 "GROUPS",
1469 "HASH",
1470 "IGNORE",
1471 "LATERAL",
1472 "LOOKUP",
1473 "MERGE",
1474 "NEW",
1475 "NO",
1476 "NULLS",
1477 "OF",
1478 "OVER",
1479 "PARTITION",
1480 "PRECEDING",
1481 "PROTO",
1482 "RANGE",
1483 "RECURSIVE",
1484 "RESPECT",
1485 "ROLLUP",
1486 "STRUCT",
1487 "TABLESAMPLE",
1488 "TREAT",
1489 "UNBOUNDED",
1490 "WITHIN",
1491 ]
1492 .iter()
1493 .copied(),
1494 );
1495 }
1496 Some(DialectType::Snowflake) => {
1497 words.extend(
1498 [
1499 "ACCOUNT",
1500 "BOTH",
1501 "CONNECT",
1502 "FOLLOWING",
1503 "ILIKE",
1504 "INCREMENT",
1505 "ISSUE",
1506 "LATERAL",
1507 "LEADING",
1508 "LOCALTIME",
1509 "LOCALTIMESTAMP",
1510 "MINUS",
1511 "QUALIFY",
1512 "REGEXP",
1513 "RLIKE",
1514 "SOME",
1515 "START",
1516 "TABLESAMPLE",
1517 "TOP",
1518 "TRAILING",
1519 "TRY_CAST",
1520 ]
1521 .iter()
1522 .copied(),
1523 );
1524 }
1525 Some(DialectType::TSQL) | Some(DialectType::Fabric) => {
1526 words.extend(
1527 [
1528 "BACKUP",
1529 "BREAK",
1530 "BROWSE",
1531 "BULK",
1532 "CASCADE",
1533 "CHECKPOINT",
1534 "CLOSE",
1535 "CLUSTERED",
1536 "COALESCE",
1537 "COMPUTE",
1538 "CONTAINS",
1539 "CONTAINSTABLE",
1540 "CONTINUE",
1541 "CONVERT",
1542 "DBCC",
1543 "DEALLOCATE",
1544 "DENY",
1545 "DISK",
1546 "DISTRIBUTED",
1547 "DUMP",
1548 "ERRLVL",
1549 "EXEC",
1550 "EXECUTE",
1551 "EXIT",
1552 "EXTERNAL",
1553 "FILE",
1554 "FILLFACTOR",
1555 "FREETEXT",
1556 "FREETEXTTABLE",
1557 "FUNCTION",
1558 "GOTO",
1559 "HOLDLOCK",
1560 "IDENTITY",
1561 "IDENTITYCOL",
1562 "IDENTITY_INSERT",
1563 "KILL",
1564 "LINENO",
1565 "MERGE",
1566 "NONCLUSTERED",
1567 "NULLIF",
1568 "OF",
1569 "OFF",
1570 "OFFSETS",
1571 "OPEN",
1572 "OPENDATASOURCE",
1573 "OPENQUERY",
1574 "OPENROWSET",
1575 "OPENXML",
1576 "OVER",
1577 "PERCENT",
1578 "PIVOT",
1579 "PLAN",
1580 "PRINT",
1581 "PROC",
1582 "PROCEDURE",
1583 "PUBLIC",
1584 "RAISERROR",
1585 "READ",
1586 "READTEXT",
1587 "RECONFIGURE",
1588 "REPLICATION",
1589 "RESTORE",
1590 "RESTRICT",
1591 "REVERT",
1592 "ROWCOUNT",
1593 "ROWGUIDCOL",
1594 "RULE",
1595 "SAVE",
1596 "SECURITYAUDIT",
1597 "SEMANTICKEYPHRASETABLE",
1598 "SEMANTICSIMILARITYDETAILSTABLE",
1599 "SEMANTICSIMILARITYTABLE",
1600 "SETUSER",
1601 "SHUTDOWN",
1602 "STATISTICS",
1603 "SYSTEM_USER",
1604 "TEXTSIZE",
1605 "TOP",
1606 "TRAN",
1607 "TRANSACTION",
1608 "TRIGGER",
1609 "TSEQUAL",
1610 "UNPIVOT",
1611 "UPDATETEXT",
1612 "WAITFOR",
1613 "WRITETEXT",
1614 ]
1615 .iter()
1616 .copied(),
1617 );
1618 }
1619 Some(DialectType::ClickHouse) => {
1620 words.extend(
1621 [
1622 "ANTI",
1623 "ARRAY",
1624 "ASOF",
1625 "FINAL",
1626 "FORMAT",
1627 "GLOBAL",
1628 "INF",
1629 "KILL",
1630 "MATERIALIZED",
1631 "NAN",
1632 "PREWHERE",
1633 "SAMPLE",
1634 "SEMI",
1635 "SETTINGS",
1636 "TOP",
1637 ]
1638 .iter()
1639 .copied(),
1640 );
1641 }
1642 Some(DialectType::DuckDB) => {
1643 words.extend(
1644 [
1645 "ANALYSE",
1646 "ANALYZE",
1647 "ARRAY",
1648 "BOTH",
1649 "LATERAL",
1650 "LEADING",
1651 "LOCALTIME",
1652 "LOCALTIMESTAMP",
1653 "PLACING",
1654 "QUALIFY",
1655 "SIMILAR",
1656 "TABLESAMPLE",
1657 "TRAILING",
1658 ]
1659 .iter()
1660 .copied(),
1661 );
1662 }
1663 Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks) => {
1664 words.extend(
1665 [
1666 "BOTH",
1667 "CLUSTER",
1668 "DISTRIBUTE",
1669 "EXCHANGE",
1670 "EXTENDED",
1671 "FUNCTION",
1672 "LATERAL",
1673 "LEADING",
1674 "MACRO",
1675 "OVER",
1676 "PARTITION",
1677 "PERCENT",
1678 "RANGE",
1679 "READS",
1680 "REDUCE",
1681 "REGEXP",
1682 "REVOKE",
1683 "RLIKE",
1684 "ROLLUP",
1685 "SEMI",
1686 "SORT",
1687 "TABLESAMPLE",
1688 "TRAILING",
1689 "TRANSFORM",
1690 "UNBOUNDED",
1691 "UNIQUEJOIN",
1692 ]
1693 .iter()
1694 .copied(),
1695 );
1696 }
1697 Some(DialectType::Trino) | Some(DialectType::Presto) | Some(DialectType::Athena) => {
1698 words.extend(
1699 [
1700 "CUBE",
1701 "DEALLOCATE",
1702 "DESCRIBE",
1703 "EXECUTE",
1704 "EXTRACT",
1705 "GROUPING",
1706 "LATERAL",
1707 "LOCALTIME",
1708 "LOCALTIMESTAMP",
1709 "NORMALIZE",
1710 "PREPARE",
1711 "ROLLUP",
1712 "SOME",
1713 "TABLESAMPLE",
1714 "UESCAPE",
1715 "UNNEST",
1716 ]
1717 .iter()
1718 .copied(),
1719 );
1720 }
1721 Some(DialectType::Oracle) => {
1722 words.extend(
1723 [
1724 "ACCESS",
1725 "AUDIT",
1726 "CLUSTER",
1727 "COMMENT",
1728 "COMPRESS",
1729 "CONNECT",
1730 "EXCLUSIVE",
1731 "FILE",
1732 "IDENTIFIED",
1733 "IMMEDIATE",
1734 "INCREMENT",
1735 "INITIAL",
1736 "LEVEL",
1737 "LOCK",
1738 "LONG",
1739 "MAXEXTENTS",
1740 "MINUS",
1741 "MODE",
1742 "NOAUDIT",
1743 "NOCOMPRESS",
1744 "NOWAIT",
1745 "NUMBER",
1746 "OF",
1747 "OFFLINE",
1748 "ONLINE",
1749 "PCTFREE",
1750 "PRIOR",
1751 "RAW",
1752 "RENAME",
1753 "RESOURCE",
1754 "REVOKE",
1755 "SHARE",
1756 "SIZE",
1757 "START",
1758 "SUCCESSFUL",
1759 "SYNONYM",
1760 "SYSDATE",
1761 "TRIGGER",
1762 "UID",
1763 "VALIDATE",
1764 "VARCHAR2",
1765 "WHENEVER",
1766 ]
1767 .iter()
1768 .copied(),
1769 );
1770 }
1771 Some(DialectType::Redshift) => {
1772 words.extend(
1773 [
1774 "AZ64",
1775 "BZIP2",
1776 "DELTA",
1777 "DELTA32K",
1778 "DISTSTYLE",
1779 "ENCODE",
1780 "GZIP",
1781 "ILIKE",
1782 "LIMIT",
1783 "LUNS",
1784 "LZO",
1785 "LZOP",
1786 "MOSTLY13",
1787 "MOSTLY32",
1788 "MOSTLY8",
1789 "RAW",
1790 "SIMILAR",
1791 "SNAPSHOT",
1792 "SORTKEY",
1793 "SYSDATE",
1794 "TOP",
1795 "ZSTD",
1796 ]
1797 .iter()
1798 .copied(),
1799 );
1800 }
1801 _ => {
1802 words.extend(
1804 [
1805 "ANALYZE",
1806 "ARRAY",
1807 "BOTH",
1808 "CUBE",
1809 "GROUPING",
1810 "LATERAL",
1811 "LEADING",
1812 "LOCALTIME",
1813 "LOCALTIMESTAMP",
1814 "OVER",
1815 "PARTITION",
1816 "QUALIFY",
1817 "RANGE",
1818 "ROLLUP",
1819 "SIMILAR",
1820 "SOME",
1821 "TABLESAMPLE",
1822 "TRAILING",
1823 ]
1824 .iter()
1825 .copied(),
1826 );
1827 }
1828 }
1829
1830 words
1831}
1832
1833fn needs_quoting(name: &str, reserved_words: &HashSet<&str>) -> bool {
1841 if name.is_empty() {
1842 return false;
1843 }
1844
1845 if name.as_bytes()[0].is_ascii_digit() {
1847 return true;
1848 }
1849
1850 if !name.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') {
1852 return true;
1853 }
1854
1855 let upper = name.to_uppercase();
1857 reserved_words.contains(upper.as_str())
1858}
1859
1860fn maybe_quote(id: &mut Identifier, reserved_words: &HashSet<&str>) {
1862 if id.quoted || id.name.is_empty() || id.name == "*" {
1865 return;
1866 }
1867 if needs_quoting(&id.name, reserved_words) {
1868 id.quoted = true;
1869 }
1870}
1871
1872fn quote_identifiers_recursive(expr: &mut Expression, reserved_words: &HashSet<&str>) {
1874 match expr {
1875 Expression::Identifier(id) => {
1877 maybe_quote(id, reserved_words);
1878 }
1879
1880 Expression::Column(col) => {
1881 maybe_quote(&mut col.name, reserved_words);
1882 if let Some(ref mut table) = col.table {
1883 maybe_quote(table, reserved_words);
1884 }
1885 }
1886
1887 Expression::Table(table_ref) => {
1888 maybe_quote(&mut table_ref.name, reserved_words);
1889 if let Some(ref mut schema) = table_ref.schema {
1890 maybe_quote(schema, reserved_words);
1891 }
1892 if let Some(ref mut catalog) = table_ref.catalog {
1893 maybe_quote(catalog, reserved_words);
1894 }
1895 if let Some(ref mut alias) = table_ref.alias {
1896 maybe_quote(alias, reserved_words);
1897 }
1898 for ca in &mut table_ref.column_aliases {
1899 maybe_quote(ca, reserved_words);
1900 }
1901 for p in &mut table_ref.partitions {
1902 maybe_quote(p, reserved_words);
1903 }
1904 for h in &mut table_ref.hints {
1906 quote_identifiers_recursive(h, reserved_words);
1907 }
1908 if let Some(ref mut ver) = table_ref.version {
1909 quote_identifiers_recursive(&mut ver.this, reserved_words);
1910 if let Some(ref mut e) = ver.expression {
1911 quote_identifiers_recursive(e, reserved_words);
1912 }
1913 }
1914 }
1915
1916 Expression::Star(star) => {
1917 if let Some(ref mut table) = star.table {
1918 maybe_quote(table, reserved_words);
1919 }
1920 if let Some(ref mut except_ids) = star.except {
1921 for id in except_ids {
1922 maybe_quote(id, reserved_words);
1923 }
1924 }
1925 if let Some(ref mut replace_aliases) = star.replace {
1926 for alias in replace_aliases {
1927 maybe_quote(&mut alias.alias, reserved_words);
1928 quote_identifiers_recursive(&mut alias.this, reserved_words);
1929 }
1930 }
1931 if let Some(ref mut rename_pairs) = star.rename {
1932 for (from, to) in rename_pairs {
1933 maybe_quote(from, reserved_words);
1934 maybe_quote(to, reserved_words);
1935 }
1936 }
1937 }
1938
1939 Expression::Alias(alias) => {
1941 maybe_quote(&mut alias.alias, reserved_words);
1942 for ca in &mut alias.column_aliases {
1943 maybe_quote(ca, reserved_words);
1944 }
1945 quote_identifiers_recursive(&mut alias.this, reserved_words);
1946 }
1947
1948 Expression::Select(select) => {
1950 for e in &mut select.expressions {
1951 quote_identifiers_recursive(e, reserved_words);
1952 }
1953 if let Some(ref mut from) = select.from {
1954 for e in &mut from.expressions {
1955 quote_identifiers_recursive(e, reserved_words);
1956 }
1957 }
1958 for join in &mut select.joins {
1959 quote_join(join, reserved_words);
1960 }
1961 for lv in &mut select.lateral_views {
1962 quote_lateral_view(lv, reserved_words);
1963 }
1964 if let Some(ref mut prewhere) = select.prewhere {
1965 quote_identifiers_recursive(prewhere, reserved_words);
1966 }
1967 if let Some(ref mut wh) = select.where_clause {
1968 quote_identifiers_recursive(&mut wh.this, reserved_words);
1969 }
1970 if let Some(ref mut gb) = select.group_by {
1971 for e in &mut gb.expressions {
1972 quote_identifiers_recursive(e, reserved_words);
1973 }
1974 }
1975 if let Some(ref mut hv) = select.having {
1976 quote_identifiers_recursive(&mut hv.this, reserved_words);
1977 }
1978 if let Some(ref mut q) = select.qualify {
1979 quote_identifiers_recursive(&mut q.this, reserved_words);
1980 }
1981 if let Some(ref mut ob) = select.order_by {
1982 for o in &mut ob.expressions {
1983 quote_identifiers_recursive(&mut o.this, reserved_words);
1984 }
1985 }
1986 if let Some(ref mut lim) = select.limit {
1987 quote_identifiers_recursive(&mut lim.this, reserved_words);
1988 }
1989 if let Some(ref mut off) = select.offset {
1990 quote_identifiers_recursive(&mut off.this, reserved_words);
1991 }
1992 if let Some(ref mut with) = select.with {
1993 quote_with(with, reserved_words);
1994 }
1995 if let Some(ref mut windows) = select.windows {
1996 for nw in windows {
1997 maybe_quote(&mut nw.name, reserved_words);
1998 quote_over(&mut nw.spec, reserved_words);
1999 }
2000 }
2001 if let Some(ref mut distinct_on) = select.distinct_on {
2002 for e in distinct_on {
2003 quote_identifiers_recursive(e, reserved_words);
2004 }
2005 }
2006 if let Some(ref mut limit_by) = select.limit_by {
2007 for e in limit_by {
2008 quote_identifiers_recursive(e, reserved_words);
2009 }
2010 }
2011 if let Some(ref mut settings) = select.settings {
2012 for e in settings {
2013 quote_identifiers_recursive(e, reserved_words);
2014 }
2015 }
2016 if let Some(ref mut format) = select.format {
2017 quote_identifiers_recursive(format, reserved_words);
2018 }
2019 }
2020
2021 Expression::Union(u) => {
2023 quote_identifiers_recursive(&mut u.left, reserved_words);
2024 quote_identifiers_recursive(&mut u.right, reserved_words);
2025 if let Some(ref mut with) = u.with {
2026 quote_with(with, reserved_words);
2027 }
2028 if let Some(ref mut ob) = u.order_by {
2029 for o in &mut ob.expressions {
2030 quote_identifiers_recursive(&mut o.this, reserved_words);
2031 }
2032 }
2033 if let Some(ref mut lim) = u.limit {
2034 quote_identifiers_recursive(lim, reserved_words);
2035 }
2036 if let Some(ref mut off) = u.offset {
2037 quote_identifiers_recursive(off, reserved_words);
2038 }
2039 }
2040 Expression::Intersect(i) => {
2041 quote_identifiers_recursive(&mut i.left, reserved_words);
2042 quote_identifiers_recursive(&mut i.right, reserved_words);
2043 if let Some(ref mut with) = i.with {
2044 quote_with(with, reserved_words);
2045 }
2046 if let Some(ref mut ob) = i.order_by {
2047 for o in &mut ob.expressions {
2048 quote_identifiers_recursive(&mut o.this, reserved_words);
2049 }
2050 }
2051 }
2052 Expression::Except(e) => {
2053 quote_identifiers_recursive(&mut e.left, reserved_words);
2054 quote_identifiers_recursive(&mut e.right, reserved_words);
2055 if let Some(ref mut with) = e.with {
2056 quote_with(with, reserved_words);
2057 }
2058 if let Some(ref mut ob) = e.order_by {
2059 for o in &mut ob.expressions {
2060 quote_identifiers_recursive(&mut o.this, reserved_words);
2061 }
2062 }
2063 }
2064
2065 Expression::Subquery(sq) => {
2067 quote_identifiers_recursive(&mut sq.this, reserved_words);
2068 if let Some(ref mut alias) = sq.alias {
2069 maybe_quote(alias, reserved_words);
2070 }
2071 for ca in &mut sq.column_aliases {
2072 maybe_quote(ca, reserved_words);
2073 }
2074 if let Some(ref mut ob) = sq.order_by {
2075 for o in &mut ob.expressions {
2076 quote_identifiers_recursive(&mut o.this, reserved_words);
2077 }
2078 }
2079 }
2080
2081 Expression::Insert(ins) => {
2083 quote_table_ref(&mut ins.table, reserved_words);
2084 for c in &mut ins.columns {
2085 maybe_quote(c, reserved_words);
2086 }
2087 for row in &mut ins.values {
2088 for e in row {
2089 quote_identifiers_recursive(e, reserved_words);
2090 }
2091 }
2092 if let Some(ref mut q) = ins.query {
2093 quote_identifiers_recursive(q, reserved_words);
2094 }
2095 for (id, val) in &mut ins.partition {
2096 maybe_quote(id, reserved_words);
2097 if let Some(ref mut v) = val {
2098 quote_identifiers_recursive(v, reserved_words);
2099 }
2100 }
2101 for e in &mut ins.returning {
2102 quote_identifiers_recursive(e, reserved_words);
2103 }
2104 if let Some(ref mut on_conflict) = ins.on_conflict {
2105 quote_identifiers_recursive(on_conflict, reserved_words);
2106 }
2107 if let Some(ref mut with) = ins.with {
2108 quote_with(with, reserved_words);
2109 }
2110 if let Some(ref mut alias) = ins.alias {
2111 maybe_quote(alias, reserved_words);
2112 }
2113 if let Some(ref mut src_alias) = ins.source_alias {
2114 maybe_quote(src_alias, reserved_words);
2115 }
2116 }
2117
2118 Expression::Update(upd) => {
2119 quote_table_ref(&mut upd.table, reserved_words);
2120 for tr in &mut upd.extra_tables {
2121 quote_table_ref(tr, reserved_words);
2122 }
2123 for join in &mut upd.table_joins {
2124 quote_join(join, reserved_words);
2125 }
2126 for (id, val) in &mut upd.set {
2127 maybe_quote(id, reserved_words);
2128 quote_identifiers_recursive(val, reserved_words);
2129 }
2130 if let Some(ref mut from) = upd.from_clause {
2131 for e in &mut from.expressions {
2132 quote_identifiers_recursive(e, reserved_words);
2133 }
2134 }
2135 for join in &mut upd.from_joins {
2136 quote_join(join, reserved_words);
2137 }
2138 if let Some(ref mut wh) = upd.where_clause {
2139 quote_identifiers_recursive(&mut wh.this, reserved_words);
2140 }
2141 for e in &mut upd.returning {
2142 quote_identifiers_recursive(e, reserved_words);
2143 }
2144 if let Some(ref mut with) = upd.with {
2145 quote_with(with, reserved_words);
2146 }
2147 }
2148
2149 Expression::Delete(del) => {
2150 quote_table_ref(&mut del.table, reserved_words);
2151 if let Some(ref mut alias) = del.alias {
2152 maybe_quote(alias, reserved_words);
2153 }
2154 for tr in &mut del.using {
2155 quote_table_ref(tr, reserved_words);
2156 }
2157 if let Some(ref mut wh) = del.where_clause {
2158 quote_identifiers_recursive(&mut wh.this, reserved_words);
2159 }
2160 if let Some(ref mut with) = del.with {
2161 quote_with(with, reserved_words);
2162 }
2163 }
2164
2165 Expression::And(bin)
2167 | Expression::Or(bin)
2168 | Expression::Eq(bin)
2169 | Expression::Neq(bin)
2170 | Expression::Lt(bin)
2171 | Expression::Lte(bin)
2172 | Expression::Gt(bin)
2173 | Expression::Gte(bin)
2174 | Expression::Add(bin)
2175 | Expression::Sub(bin)
2176 | Expression::Mul(bin)
2177 | Expression::Div(bin)
2178 | Expression::Mod(bin)
2179 | Expression::BitwiseAnd(bin)
2180 | Expression::BitwiseOr(bin)
2181 | Expression::BitwiseXor(bin)
2182 | Expression::Concat(bin)
2183 | Expression::Adjacent(bin)
2184 | Expression::TsMatch(bin)
2185 | Expression::PropertyEQ(bin)
2186 | Expression::ArrayContainsAll(bin)
2187 | Expression::ArrayContainedBy(bin)
2188 | Expression::ArrayOverlaps(bin)
2189 | Expression::JSONBContainsAllTopKeys(bin)
2190 | Expression::JSONBContainsAnyTopKeys(bin)
2191 | Expression::JSONBDeleteAtPath(bin)
2192 | Expression::ExtendsLeft(bin)
2193 | Expression::ExtendsRight(bin)
2194 | Expression::Is(bin)
2195 | Expression::NullSafeEq(bin)
2196 | Expression::NullSafeNeq(bin)
2197 | Expression::Glob(bin)
2198 | Expression::Match(bin)
2199 | Expression::MemberOf(bin)
2200 | Expression::BitwiseLeftShift(bin)
2201 | Expression::BitwiseRightShift(bin) => {
2202 quote_identifiers_recursive(&mut bin.left, reserved_words);
2203 quote_identifiers_recursive(&mut bin.right, reserved_words);
2204 }
2205
2206 Expression::Like(like) | Expression::ILike(like) => {
2208 quote_identifiers_recursive(&mut like.left, reserved_words);
2209 quote_identifiers_recursive(&mut like.right, reserved_words);
2210 if let Some(ref mut esc) = like.escape {
2211 quote_identifiers_recursive(esc, reserved_words);
2212 }
2213 }
2214
2215 Expression::Not(un) | Expression::Neg(un) | Expression::BitwiseNot(un) => {
2217 quote_identifiers_recursive(&mut un.this, reserved_words);
2218 }
2219
2220 Expression::In(in_expr) => {
2222 quote_identifiers_recursive(&mut in_expr.this, reserved_words);
2223 for e in &mut in_expr.expressions {
2224 quote_identifiers_recursive(e, reserved_words);
2225 }
2226 if let Some(ref mut q) = in_expr.query {
2227 quote_identifiers_recursive(q, reserved_words);
2228 }
2229 if let Some(ref mut un) = in_expr.unnest {
2230 quote_identifiers_recursive(un, reserved_words);
2231 }
2232 }
2233
2234 Expression::Between(bw) => {
2235 quote_identifiers_recursive(&mut bw.this, reserved_words);
2236 quote_identifiers_recursive(&mut bw.low, reserved_words);
2237 quote_identifiers_recursive(&mut bw.high, reserved_words);
2238 }
2239
2240 Expression::IsNull(is_null) => {
2241 quote_identifiers_recursive(&mut is_null.this, reserved_words);
2242 }
2243
2244 Expression::IsTrue(is_tf) | Expression::IsFalse(is_tf) => {
2245 quote_identifiers_recursive(&mut is_tf.this, reserved_words);
2246 }
2247
2248 Expression::Exists(ex) => {
2249 quote_identifiers_recursive(&mut ex.this, reserved_words);
2250 }
2251
2252 Expression::Function(func) => {
2254 for arg in &mut func.args {
2255 quote_identifiers_recursive(arg, reserved_words);
2256 }
2257 }
2258
2259 Expression::AggregateFunction(agg) => {
2260 for arg in &mut agg.args {
2261 quote_identifiers_recursive(arg, reserved_words);
2262 }
2263 if let Some(ref mut filter) = agg.filter {
2264 quote_identifiers_recursive(filter, reserved_words);
2265 }
2266 for o in &mut agg.order_by {
2267 quote_identifiers_recursive(&mut o.this, reserved_words);
2268 }
2269 }
2270
2271 Expression::WindowFunction(wf) => {
2272 quote_identifiers_recursive(&mut wf.this, reserved_words);
2273 quote_over(&mut wf.over, reserved_words);
2274 }
2275
2276 Expression::Case(case) => {
2278 if let Some(ref mut operand) = case.operand {
2279 quote_identifiers_recursive(operand, reserved_words);
2280 }
2281 for (when, then) in &mut case.whens {
2282 quote_identifiers_recursive(when, reserved_words);
2283 quote_identifiers_recursive(then, reserved_words);
2284 }
2285 if let Some(ref mut else_) = case.else_ {
2286 quote_identifiers_recursive(else_, reserved_words);
2287 }
2288 }
2289
2290 Expression::Cast(cast) | Expression::TryCast(cast) | Expression::SafeCast(cast) => {
2292 quote_identifiers_recursive(&mut cast.this, reserved_words);
2293 if let Some(ref mut fmt) = cast.format {
2294 quote_identifiers_recursive(fmt, reserved_words);
2295 }
2296 }
2297
2298 Expression::Paren(paren) => {
2300 quote_identifiers_recursive(&mut paren.this, reserved_words);
2301 }
2302
2303 Expression::Annotated(ann) => {
2304 quote_identifiers_recursive(&mut ann.this, reserved_words);
2305 }
2306
2307 Expression::With(with) => {
2309 quote_with(with, reserved_words);
2310 }
2311
2312 Expression::Cte(cte) => {
2313 maybe_quote(&mut cte.alias, reserved_words);
2314 for c in &mut cte.columns {
2315 maybe_quote(c, reserved_words);
2316 }
2317 quote_identifiers_recursive(&mut cte.this, reserved_words);
2318 }
2319
2320 Expression::From(from) => {
2322 for e in &mut from.expressions {
2323 quote_identifiers_recursive(e, reserved_words);
2324 }
2325 }
2326
2327 Expression::Join(join) => {
2328 quote_join(join, reserved_words);
2329 }
2330
2331 Expression::JoinedTable(jt) => {
2332 quote_identifiers_recursive(&mut jt.left, reserved_words);
2333 for join in &mut jt.joins {
2334 quote_join(join, reserved_words);
2335 }
2336 if let Some(ref mut alias) = jt.alias {
2337 maybe_quote(alias, reserved_words);
2338 }
2339 }
2340
2341 Expression::Where(wh) => {
2342 quote_identifiers_recursive(&mut wh.this, reserved_words);
2343 }
2344
2345 Expression::GroupBy(gb) => {
2346 for e in &mut gb.expressions {
2347 quote_identifiers_recursive(e, reserved_words);
2348 }
2349 }
2350
2351 Expression::Having(hv) => {
2352 quote_identifiers_recursive(&mut hv.this, reserved_words);
2353 }
2354
2355 Expression::OrderBy(ob) => {
2356 for o in &mut ob.expressions {
2357 quote_identifiers_recursive(&mut o.this, reserved_words);
2358 }
2359 }
2360
2361 Expression::Ordered(ord) => {
2362 quote_identifiers_recursive(&mut ord.this, reserved_words);
2363 }
2364
2365 Expression::Limit(lim) => {
2366 quote_identifiers_recursive(&mut lim.this, reserved_words);
2367 }
2368
2369 Expression::Offset(off) => {
2370 quote_identifiers_recursive(&mut off.this, reserved_words);
2371 }
2372
2373 Expression::Qualify(q) => {
2374 quote_identifiers_recursive(&mut q.this, reserved_words);
2375 }
2376
2377 Expression::Window(ws) => {
2378 for e in &mut ws.partition_by {
2379 quote_identifiers_recursive(e, reserved_words);
2380 }
2381 for o in &mut ws.order_by {
2382 quote_identifiers_recursive(&mut o.this, reserved_words);
2383 }
2384 }
2385
2386 Expression::Over(over) => {
2387 quote_over(over, reserved_words);
2388 }
2389
2390 Expression::WithinGroup(wg) => {
2391 quote_identifiers_recursive(&mut wg.this, reserved_words);
2392 for o in &mut wg.order_by {
2393 quote_identifiers_recursive(&mut o.this, reserved_words);
2394 }
2395 }
2396
2397 Expression::Pivot(piv) => {
2399 quote_identifiers_recursive(&mut piv.this, reserved_words);
2400 for e in &mut piv.expressions {
2401 quote_identifiers_recursive(e, reserved_words);
2402 }
2403 for f in &mut piv.fields {
2404 quote_identifiers_recursive(f, reserved_words);
2405 }
2406 if let Some(ref mut alias) = piv.alias {
2407 maybe_quote(alias, reserved_words);
2408 }
2409 }
2410
2411 Expression::Unpivot(unpiv) => {
2412 quote_identifiers_recursive(&mut unpiv.this, reserved_words);
2413 maybe_quote(&mut unpiv.value_column, reserved_words);
2414 maybe_quote(&mut unpiv.name_column, reserved_words);
2415 for e in &mut unpiv.columns {
2416 quote_identifiers_recursive(e, reserved_words);
2417 }
2418 if let Some(ref mut alias) = unpiv.alias {
2419 maybe_quote(alias, reserved_words);
2420 }
2421 }
2422
2423 Expression::Values(vals) => {
2425 for tuple in &mut vals.expressions {
2426 for e in &mut tuple.expressions {
2427 quote_identifiers_recursive(e, reserved_words);
2428 }
2429 }
2430 if let Some(ref mut alias) = vals.alias {
2431 maybe_quote(alias, reserved_words);
2432 }
2433 for ca in &mut vals.column_aliases {
2434 maybe_quote(ca, reserved_words);
2435 }
2436 }
2437
2438 Expression::Array(arr) => {
2440 for e in &mut arr.expressions {
2441 quote_identifiers_recursive(e, reserved_words);
2442 }
2443 }
2444
2445 Expression::Struct(st) => {
2446 for (_name, e) in &mut st.fields {
2447 quote_identifiers_recursive(e, reserved_words);
2448 }
2449 }
2450
2451 Expression::Tuple(tup) => {
2452 for e in &mut tup.expressions {
2453 quote_identifiers_recursive(e, reserved_words);
2454 }
2455 }
2456
2457 Expression::Subscript(sub) => {
2459 quote_identifiers_recursive(&mut sub.this, reserved_words);
2460 quote_identifiers_recursive(&mut sub.index, reserved_words);
2461 }
2462
2463 Expression::Dot(dot) => {
2464 quote_identifiers_recursive(&mut dot.this, reserved_words);
2465 maybe_quote(&mut dot.field, reserved_words);
2466 }
2467
2468 Expression::ScopeResolution(sr) => {
2469 if let Some(ref mut this) = sr.this {
2470 quote_identifiers_recursive(this, reserved_words);
2471 }
2472 quote_identifiers_recursive(&mut sr.expression, reserved_words);
2473 }
2474
2475 Expression::Lateral(lat) => {
2477 quote_identifiers_recursive(&mut lat.this, reserved_words);
2478 }
2480
2481 Expression::DPipe(dpipe) => {
2483 quote_identifiers_recursive(&mut dpipe.this, reserved_words);
2484 quote_identifiers_recursive(&mut dpipe.expression, reserved_words);
2485 }
2486
2487 Expression::Merge(merge) => {
2489 quote_identifiers_recursive(&mut merge.this, reserved_words);
2490 quote_identifiers_recursive(&mut merge.using, reserved_words);
2491 if let Some(ref mut on) = merge.on {
2492 quote_identifiers_recursive(on, reserved_words);
2493 }
2494 if let Some(ref mut whens) = merge.whens {
2495 quote_identifiers_recursive(whens, reserved_words);
2496 }
2497 if let Some(ref mut with) = merge.with_ {
2498 quote_identifiers_recursive(with, reserved_words);
2499 }
2500 if let Some(ref mut ret) = merge.returning {
2501 quote_identifiers_recursive(ret, reserved_words);
2502 }
2503 }
2504
2505 Expression::LateralView(lv) => {
2507 quote_lateral_view(lv, reserved_words);
2508 }
2509
2510 Expression::Anonymous(anon) => {
2512 quote_identifiers_recursive(&mut anon.this, reserved_words);
2513 for e in &mut anon.expressions {
2514 quote_identifiers_recursive(e, reserved_words);
2515 }
2516 }
2517
2518 Expression::Filter(filter) => {
2520 quote_identifiers_recursive(&mut filter.this, reserved_words);
2521 quote_identifiers_recursive(&mut filter.expression, reserved_words);
2522 }
2523
2524 Expression::Returning(ret) => {
2526 for e in &mut ret.expressions {
2527 quote_identifiers_recursive(e, reserved_words);
2528 }
2529 }
2530
2531 Expression::BracedWildcard(inner) => {
2533 quote_identifiers_recursive(inner, reserved_words);
2534 }
2535
2536 Expression::ReturnStmt(inner) => {
2538 quote_identifiers_recursive(inner, reserved_words);
2539 }
2540
2541 Expression::Literal(_)
2543 | Expression::Boolean(_)
2544 | Expression::Null(_)
2545 | Expression::DataType(_)
2546 | Expression::Raw(_)
2547 | Expression::Placeholder(_)
2548 | Expression::CurrentDate(_)
2549 | Expression::CurrentTime(_)
2550 | Expression::CurrentTimestamp(_)
2551 | Expression::CurrentTimestampLTZ(_)
2552 | Expression::SessionUser(_)
2553 | Expression::RowNumber(_)
2554 | Expression::Rank(_)
2555 | Expression::DenseRank(_)
2556 | Expression::PercentRank(_)
2557 | Expression::CumeDist(_)
2558 | Expression::Random(_)
2559 | Expression::Pi(_)
2560 | Expression::JSONPathRoot(_) => {
2561 }
2563
2564 _ => {}
2568 }
2569}
2570
2571fn quote_join(join: &mut Join, reserved_words: &HashSet<&str>) {
2573 quote_identifiers_recursive(&mut join.this, reserved_words);
2574 if let Some(ref mut on) = join.on {
2575 quote_identifiers_recursive(on, reserved_words);
2576 }
2577 for id in &mut join.using {
2578 maybe_quote(id, reserved_words);
2579 }
2580 if let Some(ref mut mc) = join.match_condition {
2581 quote_identifiers_recursive(mc, reserved_words);
2582 }
2583 for piv in &mut join.pivots {
2584 quote_identifiers_recursive(piv, reserved_words);
2585 }
2586}
2587
2588fn quote_with(with: &mut With, reserved_words: &HashSet<&str>) {
2590 for cte in &mut with.ctes {
2591 maybe_quote(&mut cte.alias, reserved_words);
2592 for c in &mut cte.columns {
2593 maybe_quote(c, reserved_words);
2594 }
2595 for k in &mut cte.key_expressions {
2596 maybe_quote(k, reserved_words);
2597 }
2598 quote_identifiers_recursive(&mut cte.this, reserved_words);
2599 }
2600}
2601
2602fn quote_over(over: &mut Over, reserved_words: &HashSet<&str>) {
2604 if let Some(ref mut wn) = over.window_name {
2605 maybe_quote(wn, reserved_words);
2606 }
2607 for e in &mut over.partition_by {
2608 quote_identifiers_recursive(e, reserved_words);
2609 }
2610 for o in &mut over.order_by {
2611 quote_identifiers_recursive(&mut o.this, reserved_words);
2612 }
2613 if let Some(ref mut alias) = over.alias {
2614 maybe_quote(alias, reserved_words);
2615 }
2616}
2617
2618fn quote_table_ref(table_ref: &mut TableRef, reserved_words: &HashSet<&str>) {
2620 maybe_quote(&mut table_ref.name, reserved_words);
2621 if let Some(ref mut schema) = table_ref.schema {
2622 maybe_quote(schema, reserved_words);
2623 }
2624 if let Some(ref mut catalog) = table_ref.catalog {
2625 maybe_quote(catalog, reserved_words);
2626 }
2627 if let Some(ref mut alias) = table_ref.alias {
2628 maybe_quote(alias, reserved_words);
2629 }
2630 for ca in &mut table_ref.column_aliases {
2631 maybe_quote(ca, reserved_words);
2632 }
2633 for p in &mut table_ref.partitions {
2634 maybe_quote(p, reserved_words);
2635 }
2636 for h in &mut table_ref.hints {
2637 quote_identifiers_recursive(h, reserved_words);
2638 }
2639}
2640
2641fn quote_lateral_view(lv: &mut LateralView, reserved_words: &HashSet<&str>) {
2643 quote_identifiers_recursive(&mut lv.this, reserved_words);
2644 if let Some(ref mut ta) = lv.table_alias {
2645 maybe_quote(ta, reserved_words);
2646 }
2647 for ca in &mut lv.column_aliases {
2648 maybe_quote(ca, reserved_words);
2649 }
2650}
2651
2652pub fn quote_identifiers(expression: Expression, dialect: Option<DialectType>) -> Expression {
2663 let reserved_words = get_reserved_words(dialect);
2664 let mut result = expression;
2665 quote_identifiers_recursive(&mut result, &reserved_words);
2666 result
2667}
2668
2669pub fn pushdown_cte_alias_columns(_scope: &Scope) {
2674 }
2677
2678fn pushdown_cte_alias_columns_with(with: &mut With) {
2679 for cte in &mut with.ctes {
2680 if cte.columns.is_empty() {
2681 continue;
2682 }
2683
2684 if let Expression::Select(select) = &mut cte.this {
2685 let mut next_expressions = Vec::with_capacity(select.expressions.len());
2686
2687 for (i, projection) in select.expressions.iter().enumerate() {
2688 let Some(alias_name) = cte.columns.get(i) else {
2689 next_expressions.push(projection.clone());
2690 continue;
2691 };
2692
2693 match projection {
2694 Expression::Alias(existing) => {
2695 let mut aliased = existing.clone();
2696 aliased.alias = alias_name.clone();
2697 next_expressions.push(Expression::Alias(aliased));
2698 }
2699 _ => {
2700 next_expressions.push(create_alias(projection.clone(), &alias_name.name));
2701 }
2702 }
2703 }
2704
2705 select.expressions = next_expressions;
2706 }
2707 }
2708}
2709
2710fn get_scope_columns(scope: &Scope) -> Vec<ColumnRef> {
2716 let mut columns = Vec::new();
2717 collect_columns(&scope.expression, &mut columns);
2718 columns
2719}
2720
2721#[derive(Debug, Clone)]
2723struct ColumnRef {
2724 table: Option<String>,
2725 name: String,
2726}
2727
2728fn collect_columns(expr: &Expression, columns: &mut Vec<ColumnRef>) {
2730 match expr {
2731 Expression::Column(col) => {
2732 columns.push(ColumnRef {
2733 table: col.table.as_ref().map(|t| t.name.clone()),
2734 name: col.name.name.clone(),
2735 });
2736 }
2737 Expression::Select(select) => {
2738 for e in &select.expressions {
2739 collect_columns(e, columns);
2740 }
2741 if let Some(from) = &select.from {
2742 for e in &from.expressions {
2743 collect_columns(e, columns);
2744 }
2745 }
2746 if let Some(where_clause) = &select.where_clause {
2747 collect_columns(&where_clause.this, columns);
2748 }
2749 if let Some(group_by) = &select.group_by {
2750 for e in &group_by.expressions {
2751 collect_columns(e, columns);
2752 }
2753 }
2754 if let Some(having) = &select.having {
2755 collect_columns(&having.this, columns);
2756 }
2757 if let Some(order_by) = &select.order_by {
2758 for o in &order_by.expressions {
2759 collect_columns(&o.this, columns);
2760 }
2761 }
2762 for join in &select.joins {
2763 collect_columns(&join.this, columns);
2764 if let Some(on) = &join.on {
2765 collect_columns(on, columns);
2766 }
2767 }
2768 }
2769 Expression::Alias(alias) => {
2770 collect_columns(&alias.this, columns);
2771 }
2772 Expression::Function(func) => {
2773 for arg in &func.args {
2774 collect_columns(arg, columns);
2775 }
2776 }
2777 Expression::AggregateFunction(agg) => {
2778 for arg in &agg.args {
2779 collect_columns(arg, columns);
2780 }
2781 }
2782 Expression::And(bin)
2783 | Expression::Or(bin)
2784 | Expression::Eq(bin)
2785 | Expression::Neq(bin)
2786 | Expression::Lt(bin)
2787 | Expression::Lte(bin)
2788 | Expression::Gt(bin)
2789 | Expression::Gte(bin)
2790 | Expression::Add(bin)
2791 | Expression::Sub(bin)
2792 | Expression::Mul(bin)
2793 | Expression::Div(bin) => {
2794 collect_columns(&bin.left, columns);
2795 collect_columns(&bin.right, columns);
2796 }
2797 Expression::Not(unary) | Expression::Neg(unary) => {
2798 collect_columns(&unary.this, columns);
2799 }
2800 Expression::Paren(paren) => {
2801 collect_columns(&paren.this, columns);
2802 }
2803 Expression::Case(case) => {
2804 if let Some(operand) = &case.operand {
2805 collect_columns(operand, columns);
2806 }
2807 for (when, then) in &case.whens {
2808 collect_columns(when, columns);
2809 collect_columns(then, columns);
2810 }
2811 if let Some(else_) = &case.else_ {
2812 collect_columns(else_, columns);
2813 }
2814 }
2815 Expression::Cast(cast) => {
2816 collect_columns(&cast.this, columns);
2817 }
2818 Expression::In(in_expr) => {
2819 collect_columns(&in_expr.this, columns);
2820 for e in &in_expr.expressions {
2821 collect_columns(e, columns);
2822 }
2823 if let Some(query) = &in_expr.query {
2824 collect_columns(query, columns);
2825 }
2826 }
2827 Expression::Between(between) => {
2828 collect_columns(&between.this, columns);
2829 collect_columns(&between.low, columns);
2830 collect_columns(&between.high, columns);
2831 }
2832 Expression::Subquery(subquery) => {
2833 collect_columns(&subquery.this, columns);
2834 }
2835 _ => {}
2836 }
2837}
2838
2839fn get_unqualified_columns(scope: &Scope) -> Vec<ColumnRef> {
2841 get_scope_columns(scope)
2842 .into_iter()
2843 .filter(|c| c.table.is_none())
2844 .collect()
2845}
2846
2847fn get_external_columns(scope: &Scope) -> Vec<ColumnRef> {
2849 let source_names: HashSet<_> = scope.sources.keys().cloned().collect();
2850
2851 get_scope_columns(scope)
2852 .into_iter()
2853 .filter(|c| {
2854 if let Some(table) = &c.table {
2855 !source_names.contains(table)
2856 } else {
2857 false
2858 }
2859 })
2860 .collect()
2861}
2862
2863fn is_correlated_subquery(scope: &Scope) -> bool {
2865 scope.can_be_correlated && !get_external_columns(scope).is_empty()
2866}
2867
2868fn is_star_column(col: &Column) -> bool {
2870 col.name.name == "*"
2871}
2872
2873fn create_qualified_column(name: &str, table: Option<&str>) -> Expression {
2875 Expression::boxed_column(Column {
2876 name: Identifier::new(name),
2877 table: table.map(Identifier::new),
2878 join_mark: false,
2879 trailing_comments: vec![],
2880 span: None,
2881 inferred_type: None,
2882 })
2883}
2884
2885fn create_alias(expr: Expression, alias_name: &str) -> Expression {
2887 Expression::Alias(Box::new(Alias {
2888 this: expr,
2889 alias: Identifier::new(alias_name),
2890 column_aliases: vec![],
2891 alias_explicit_as: false,
2892 alias_keyword: None,
2893 pre_alias_comments: vec![],
2894 trailing_comments: vec![],
2895 inferred_type: None,
2896 }))
2897}
2898
2899fn get_output_name(expr: &Expression) -> Option<String> {
2901 match expr {
2902 Expression::Column(col) => Some(col.name.name.clone()),
2903 Expression::Alias(alias) => Some(alias.alias.name.clone()),
2904 Expression::Identifier(id) => Some(id.name.clone()),
2905 _ => None,
2906 }
2907}
2908
2909#[cfg(test)]
2910mod tests {
2911 use super::*;
2912 use crate::expressions::DataType;
2913 use crate::generator::Generator;
2914 use crate::parser::Parser;
2915 use crate::scope::build_scope;
2916 use crate::{MappingSchema, Schema};
2917
2918 fn gen(expr: &Expression) -> String {
2919 Generator::new().generate(expr).unwrap()
2920 }
2921
2922 fn parse(sql: &str) -> Expression {
2923 Parser::parse_sql(sql).expect("Failed to parse")[0].clone()
2924 }
2925
2926 #[test]
2927 fn test_qualify_columns_options() {
2928 let options = QualifyColumnsOptions::new()
2929 .with_expand_alias_refs(true)
2930 .with_expand_stars(false)
2931 .with_dialect(DialectType::PostgreSQL)
2932 .with_allow_partial(true);
2933
2934 assert!(options.expand_alias_refs);
2935 assert!(!options.expand_stars);
2936 assert_eq!(options.dialect, Some(DialectType::PostgreSQL));
2937 assert!(options.allow_partial_qualification);
2938 }
2939
2940 #[test]
2941 fn test_get_scope_columns() {
2942 let expr = parse("SELECT a, b FROM t WHERE c = 1");
2943 let scope = build_scope(&expr);
2944 let columns = get_scope_columns(&scope);
2945
2946 assert!(columns.iter().any(|c| c.name == "a"));
2947 assert!(columns.iter().any(|c| c.name == "b"));
2948 assert!(columns.iter().any(|c| c.name == "c"));
2949 }
2950
2951 #[test]
2952 fn test_get_unqualified_columns() {
2953 let expr = parse("SELECT t.a, b FROM t");
2954 let scope = build_scope(&expr);
2955 let unqualified = get_unqualified_columns(&scope);
2956
2957 assert!(unqualified.iter().any(|c| c.name == "b"));
2959 assert!(!unqualified.iter().any(|c| c.name == "a"));
2960 }
2961
2962 #[test]
2963 fn test_is_star_column() {
2964 let col = Column {
2965 name: Identifier::new("*"),
2966 table: Some(Identifier::new("t")),
2967 join_mark: false,
2968 trailing_comments: vec![],
2969 span: None,
2970 inferred_type: None,
2971 };
2972 assert!(is_star_column(&col));
2973
2974 let col2 = Column {
2975 name: Identifier::new("id"),
2976 table: None,
2977 join_mark: false,
2978 trailing_comments: vec![],
2979 span: None,
2980 inferred_type: None,
2981 };
2982 assert!(!is_star_column(&col2));
2983 }
2984
2985 #[test]
2986 fn test_create_qualified_column() {
2987 let expr = create_qualified_column("id", Some("users"));
2988 let sql = gen(&expr);
2989 assert!(sql.contains("users"));
2990 assert!(sql.contains("id"));
2991 }
2992
2993 #[test]
2994 fn test_create_alias() {
2995 let col = Expression::boxed_column(Column {
2996 name: Identifier::new("value"),
2997 table: None,
2998 join_mark: false,
2999 trailing_comments: vec![],
3000 span: None,
3001 inferred_type: None,
3002 });
3003 let aliased = create_alias(col, "total");
3004 let sql = gen(&aliased);
3005 assert!(sql.contains("AS") || sql.contains("total"));
3006 }
3007
3008 #[test]
3009 fn test_qualify_columns_normalizes_struct_field_access_issue_408() {
3010 let struct_type = DataType::Struct {
3011 fields: vec![crate::expressions::StructField::new(
3012 "field_value".into(),
3013 DataType::Text,
3014 )],
3015 nested: true,
3016 };
3017 let mut schema = MappingSchema::with_dialect(DialectType::DuckDB);
3018 schema
3019 .add_table(
3020 "source_table",
3021 &[
3022 ("composite_value".into(), struct_type.clone()),
3023 (
3024 "nested_items".into(),
3025 DataType::Array {
3026 element_type: Box::new(struct_type),
3027 dimension: None,
3028 },
3029 ),
3030 ],
3031 None,
3032 )
3033 .expect("schema setup");
3034
3035 let cases = [
3036 (
3037 "SELECT composite_value.field_value AS output_value FROM source_table",
3038 "source_table",
3039 "composite_value",
3040 ),
3041 (
3042 "SELECT item.field_value AS output_value FROM source_table s \
3043 CROSS JOIN UNNEST(s.nested_items) AS expanded(item)",
3044 "expanded",
3045 "item",
3046 ),
3047 ];
3048
3049 for (sql, expected_table, expected_column) in cases {
3050 let qualified = qualify_columns(
3051 parse(sql),
3052 &schema,
3053 &QualifyColumnsOptions::new().with_dialect(DialectType::DuckDB),
3054 )
3055 .unwrap_or_else(|error| panic!("qualification failed for {sql:?}: {error}"));
3056 let Expression::Select(select) = qualified else {
3057 panic!("expected SELECT");
3058 };
3059 let Expression::Alias(alias) = &select.expressions[0] else {
3060 panic!("expected aliased projection");
3061 };
3062 let Expression::Dot(dot) = &alias.this else {
3063 panic!("expected normalized Dot, got {:?}", alias.this);
3064 };
3065 let Expression::Column(base) = &dot.this else {
3066 panic!("expected column-backed Dot");
3067 };
3068
3069 assert_eq!(
3070 base.table.as_ref().map(|table| table.name.as_str()),
3071 Some(expected_table)
3072 );
3073 assert_eq!(base.name.name, expected_column);
3074 assert_eq!(dot.field.name, "field_value");
3075 }
3076 }
3077
3078 #[test]
3079 fn test_validate_qualify_columns_success() {
3080 let expr = parse("SELECT t.a, t.b FROM t");
3082 let result = validate_qualify_columns(&expr);
3083 let _ = result;
3086 }
3087
3088 #[test]
3089 fn test_collect_columns_nested() {
3090 let expr = parse("SELECT a + b, c FROM t WHERE d > 0 GROUP BY e HAVING f = 1");
3091 let mut columns = Vec::new();
3092 collect_columns(&expr, &mut columns);
3093
3094 let names: Vec<_> = columns.iter().map(|c| c.name.as_str()).collect();
3095 assert!(names.contains(&"a"));
3096 assert!(names.contains(&"b"));
3097 assert!(names.contains(&"c"));
3098 assert!(names.contains(&"d"));
3099 assert!(names.contains(&"e"));
3100 assert!(names.contains(&"f"));
3101 }
3102
3103 #[test]
3104 fn test_collect_columns_in_case() {
3105 let expr = parse("SELECT CASE WHEN a = 1 THEN b ELSE c END FROM t");
3106 let mut columns = Vec::new();
3107 collect_columns(&expr, &mut columns);
3108
3109 let names: Vec<_> = columns.iter().map(|c| c.name.as_str()).collect();
3110 assert!(names.contains(&"a"));
3111 assert!(names.contains(&"b"));
3112 assert!(names.contains(&"c"));
3113 }
3114
3115 #[test]
3116 fn test_collect_columns_in_subquery() {
3117 let expr = parse("SELECT a FROM t WHERE b IN (SELECT c FROM s)");
3118 let mut columns = Vec::new();
3119 collect_columns(&expr, &mut columns);
3120
3121 let names: Vec<_> = columns.iter().map(|c| c.name.as_str()).collect();
3122 assert!(names.contains(&"a"));
3123 assert!(names.contains(&"b"));
3124 assert!(names.contains(&"c"));
3125 }
3126
3127 #[test]
3128 fn test_qualify_outputs_basic() {
3129 let expr = parse("SELECT a, b + c FROM t");
3130 let scope = build_scope(&expr);
3131 let result = qualify_outputs(&scope);
3132 assert!(result.is_ok());
3133 }
3134
3135 #[test]
3136 fn test_qualify_columns_expands_star_with_schema() {
3137 let expr = parse("SELECT * FROM users");
3138
3139 let mut schema = MappingSchema::new();
3140 schema
3141 .add_table(
3142 "users",
3143 &[
3144 (
3145 "id".to_string(),
3146 DataType::Int {
3147 length: None,
3148 integer_spelling: false,
3149 },
3150 ),
3151 ("name".to_string(), DataType::Text),
3152 ("email".to_string(), DataType::Text),
3153 ],
3154 None,
3155 )
3156 .expect("schema setup");
3157
3158 let result =
3159 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3160 let sql = gen(&result);
3161
3162 assert!(!sql.contains("SELECT *"));
3163 assert!(sql.contains("users.id"));
3164 assert!(sql.contains("users.name"));
3165 assert!(sql.contains("users.email"));
3166 }
3167
3168 #[test]
3169 fn test_qualify_columns_expands_group_by_positions() {
3170 let expr = parse("SELECT a, b FROM t GROUP BY 1, 2");
3171
3172 let mut schema = MappingSchema::new();
3173 schema
3174 .add_table(
3175 "t",
3176 &[
3177 (
3178 "a".to_string(),
3179 DataType::Int {
3180 length: None,
3181 integer_spelling: false,
3182 },
3183 ),
3184 (
3185 "b".to_string(),
3186 DataType::Int {
3187 length: None,
3188 integer_spelling: false,
3189 },
3190 ),
3191 ],
3192 None,
3193 )
3194 .expect("schema setup");
3195
3196 let result =
3197 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3198 let sql = gen(&result);
3199
3200 assert!(!sql.contains("GROUP BY 1"));
3201 assert!(!sql.contains("GROUP BY 2"));
3202 assert!(sql.contains("GROUP BY"));
3203 assert!(sql.contains("t.a"));
3204 assert!(sql.contains("t.b"));
3205 }
3206
3207 #[test]
3212 fn test_expand_using_simple() {
3213 let expr = parse("SELECT x.b FROM x JOIN y USING (b)");
3215
3216 let mut schema = MappingSchema::new();
3217 schema
3218 .add_table(
3219 "x",
3220 &[
3221 ("a".to_string(), DataType::BigInt { length: None }),
3222 ("b".to_string(), DataType::BigInt { length: None }),
3223 ],
3224 None,
3225 )
3226 .expect("schema setup");
3227 schema
3228 .add_table(
3229 "y",
3230 &[
3231 ("b".to_string(), DataType::BigInt { length: None }),
3232 ("c".to_string(), DataType::BigInt { length: None }),
3233 ],
3234 None,
3235 )
3236 .expect("schema setup");
3237
3238 let result =
3239 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3240 let sql = gen(&result);
3241
3242 assert!(
3244 !sql.contains("USING"),
3245 "USING should be replaced with ON: {sql}"
3246 );
3247 assert!(
3248 sql.contains("ON x.b = y.b"),
3249 "ON condition should be x.b = y.b: {sql}"
3250 );
3251 assert!(sql.contains("SELECT x.b"), "SELECT should keep x.b: {sql}");
3253 }
3254
3255 #[test]
3256 fn test_expand_using_unqualified_coalesce() {
3257 let expr = parse("SELECT b FROM x JOIN y USING(b)");
3259
3260 let mut schema = MappingSchema::new();
3261 schema
3262 .add_table(
3263 "x",
3264 &[
3265 ("a".to_string(), DataType::BigInt { length: None }),
3266 ("b".to_string(), DataType::BigInt { length: None }),
3267 ],
3268 None,
3269 )
3270 .expect("schema setup");
3271 schema
3272 .add_table(
3273 "y",
3274 &[
3275 ("b".to_string(), DataType::BigInt { length: None }),
3276 ("c".to_string(), DataType::BigInt { length: None }),
3277 ],
3278 None,
3279 )
3280 .expect("schema setup");
3281
3282 let result =
3283 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3284 let sql = gen(&result);
3285
3286 assert!(
3287 sql.contains("COALESCE(x.b, y.b)"),
3288 "Unqualified USING column should become COALESCE: {sql}"
3289 );
3290 assert!(
3291 sql.contains("AS b"),
3292 "COALESCE should be aliased as 'b': {sql}"
3293 );
3294 assert!(
3295 sql.contains("ON x.b = y.b"),
3296 "ON condition should be generated: {sql}"
3297 );
3298 }
3299
3300 #[test]
3301 fn test_expand_using_with_where() {
3302 let expr = parse("SELECT b FROM x JOIN y USING(b) WHERE b = 1");
3304
3305 let mut schema = MappingSchema::new();
3306 schema
3307 .add_table(
3308 "x",
3309 &[("b".to_string(), DataType::BigInt { length: None })],
3310 None,
3311 )
3312 .expect("schema setup");
3313 schema
3314 .add_table(
3315 "y",
3316 &[("b".to_string(), DataType::BigInt { length: None })],
3317 None,
3318 )
3319 .expect("schema setup");
3320
3321 let result =
3322 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3323 let sql = gen(&result);
3324
3325 assert!(
3326 sql.contains("WHERE COALESCE(x.b, y.b)"),
3327 "WHERE should use COALESCE for USING column: {sql}"
3328 );
3329 }
3330
3331 #[test]
3332 fn test_expand_using_multi_join() {
3333 let expr = parse("SELECT b FROM x JOIN y USING(b) JOIN z USING(b)");
3335
3336 let mut schema = MappingSchema::new();
3337 for table in &["x", "y", "z"] {
3338 schema
3339 .add_table(
3340 table,
3341 &[("b".to_string(), DataType::BigInt { length: None })],
3342 None,
3343 )
3344 .expect("schema setup");
3345 }
3346
3347 let result =
3348 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3349 let sql = gen(&result);
3350
3351 assert!(
3353 sql.contains("COALESCE(x.b, y.b, z.b)"),
3354 "Should have 3-table COALESCE: {sql}"
3355 );
3356 assert!(
3358 sql.contains("ON x.b = y.b"),
3359 "First join ON condition: {sql}"
3360 );
3361 }
3362
3363 #[test]
3364 fn test_expand_using_multi_column() {
3365 let expr = parse("SELECT b, c FROM y JOIN z USING(b, c)");
3367
3368 let mut schema = MappingSchema::new();
3369 schema
3370 .add_table(
3371 "y",
3372 &[
3373 ("b".to_string(), DataType::BigInt { length: None }),
3374 ("c".to_string(), DataType::BigInt { length: None }),
3375 ],
3376 None,
3377 )
3378 .expect("schema setup");
3379 schema
3380 .add_table(
3381 "z",
3382 &[
3383 ("b".to_string(), DataType::BigInt { length: None }),
3384 ("c".to_string(), DataType::BigInt { length: None }),
3385 ],
3386 None,
3387 )
3388 .expect("schema setup");
3389
3390 let result =
3391 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3392 let sql = gen(&result);
3393
3394 assert!(
3395 sql.contains("COALESCE(y.b, z.b)"),
3396 "column 'b' should get COALESCE: {sql}"
3397 );
3398 assert!(
3399 sql.contains("COALESCE(y.c, z.c)"),
3400 "column 'c' should get COALESCE: {sql}"
3401 );
3402 assert!(
3404 sql.contains("y.b = z.b") && sql.contains("y.c = z.c"),
3405 "ON should have both equality conditions: {sql}"
3406 );
3407 }
3408
3409 #[test]
3410 fn test_expand_using_star() {
3411 let expr = parse("SELECT * FROM x JOIN y USING(b)");
3413
3414 let mut schema = MappingSchema::new();
3415 schema
3416 .add_table(
3417 "x",
3418 &[
3419 ("a".to_string(), DataType::BigInt { length: None }),
3420 ("b".to_string(), DataType::BigInt { length: None }),
3421 ],
3422 None,
3423 )
3424 .expect("schema setup");
3425 schema
3426 .add_table(
3427 "y",
3428 &[
3429 ("b".to_string(), DataType::BigInt { length: None }),
3430 ("c".to_string(), DataType::BigInt { length: None }),
3431 ],
3432 None,
3433 )
3434 .expect("schema setup");
3435
3436 let result =
3437 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3438 let sql = gen(&result);
3439
3440 assert!(
3442 sql.contains("COALESCE(x.b, y.b) AS b"),
3443 "USING column should be COALESCE in star expansion: {sql}"
3444 );
3445 assert!(sql.contains("x.a"), "non-USING column a from x: {sql}");
3447 assert!(sql.contains("y.c"), "non-USING column c from y: {sql}");
3448 let coalesce_count = sql.matches("COALESCE").count();
3450 assert_eq!(
3451 coalesce_count, 1,
3452 "b should appear only once as COALESCE: {sql}"
3453 );
3454 }
3455
3456 #[test]
3457 fn test_expand_using_table_star() {
3458 let expr = parse("SELECT x.* FROM x JOIN y USING(b)");
3460
3461 let mut schema = MappingSchema::new();
3462 schema
3463 .add_table(
3464 "x",
3465 &[
3466 ("a".to_string(), DataType::BigInt { length: None }),
3467 ("b".to_string(), DataType::BigInt { length: None }),
3468 ],
3469 None,
3470 )
3471 .expect("schema setup");
3472 schema
3473 .add_table(
3474 "y",
3475 &[
3476 ("b".to_string(), DataType::BigInt { length: None }),
3477 ("c".to_string(), DataType::BigInt { length: None }),
3478 ],
3479 None,
3480 )
3481 .expect("schema setup");
3482
3483 let result =
3484 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3485 let sql = gen(&result);
3486
3487 assert!(
3489 sql.contains("COALESCE(x.b, y.b)"),
3490 "USING column from x.* should become COALESCE: {sql}"
3491 );
3492 assert!(sql.contains("x.a"), "non-USING column a: {sql}");
3493 }
3494
3495 #[test]
3496 fn test_expand_natural_join_with_derived_table() {
3497 let expr = parse(
3498 "SELECT shared_key AS output_key FROM source_table \
3499 NATURAL JOIN (SELECT shared_key FROM source_table) AS derived",
3500 );
3501
3502 let mut schema = MappingSchema::new();
3503 schema
3504 .add_table(
3505 "source_table",
3506 &[(
3507 "shared_key".to_string(),
3508 DataType::VarChar {
3509 length: None,
3510 parenthesized_length: false,
3511 },
3512 )],
3513 None,
3514 )
3515 .expect("schema setup");
3516
3517 let result = qualify_columns(
3518 expr,
3519 &schema,
3520 &QualifyColumnsOptions::new().with_dialect(DialectType::DuckDB),
3521 )
3522 .expect("qualify");
3523 let sql = gen(&result);
3524
3525 assert!(!sql.contains("NATURAL"), "NATURAL should expand: {sql}");
3526 assert!(
3527 sql.contains("ON source_table.shared_key = derived.shared_key"),
3528 "common column should become an equality condition: {sql}"
3529 );
3530 assert!(
3531 sql.contains("COALESCE(source_table.shared_key, derived.shared_key) AS output_key"),
3532 "merged projection should preserve both sources: {sql}"
3533 );
3534 }
3535
3536 #[test]
3537 fn test_expand_natural_join_star_and_chained_sources() {
3538 let expr = parse("SELECT * FROM x NATURAL JOIN y NATURAL JOIN z");
3539
3540 let mut schema = MappingSchema::new();
3541 schema
3542 .add_table(
3543 "x",
3544 &[
3545 ("a".to_string(), DataType::BigInt { length: None }),
3546 ("shared".to_string(), DataType::BigInt { length: None }),
3547 ],
3548 None,
3549 )
3550 .expect("schema setup");
3551 schema
3552 .add_table(
3553 "y",
3554 &[
3555 ("shared".to_string(), DataType::BigInt { length: None }),
3556 ("b".to_string(), DataType::BigInt { length: None }),
3557 ],
3558 None,
3559 )
3560 .expect("schema setup");
3561 schema
3562 .add_table(
3563 "z",
3564 &[
3565 ("shared".to_string(), DataType::BigInt { length: None }),
3566 ("c".to_string(), DataType::BigInt { length: None }),
3567 ],
3568 None,
3569 )
3570 .expect("schema setup");
3571
3572 let result =
3573 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3574 let sql = gen(&result);
3575
3576 assert!(!sql.contains("NATURAL"), "both joins should expand: {sql}");
3577 assert!(sql.contains("ON x.shared = y.shared"), "first join: {sql}");
3578 assert!(sql.contains("ON x.shared = z.shared"), "second join: {sql}");
3579 assert_eq!(
3580 sql.matches("COALESCE(x.shared, y.shared, z.shared) AS shared")
3581 .count(),
3582 1,
3583 "merged star column should be emitted once: {sql}"
3584 );
3585 assert!(sql.contains("x.a"), "left-only column should remain: {sql}");
3586 assert!(
3587 sql.contains("y.b"),
3588 "middle-only column should remain: {sql}"
3589 );
3590 assert!(
3591 sql.contains("z.c"),
3592 "right-only column should remain: {sql}"
3593 );
3594 }
3595
3596 #[test]
3597 fn test_expand_natural_outer_join_kinds() {
3598 let mut schema = MappingSchema::new();
3599 for table in ["x", "y"] {
3600 schema
3601 .add_table(
3602 table,
3603 &[("shared".to_string(), DataType::BigInt { length: None })],
3604 None,
3605 )
3606 .expect("schema setup");
3607 }
3608
3609 for (input_kind, output_kind) in [
3610 ("NATURAL LEFT JOIN", "LEFT JOIN"),
3611 ("NATURAL RIGHT JOIN", "RIGHT JOIN"),
3612 ("NATURAL FULL JOIN", "FULL JOIN"),
3613 ] {
3614 let expr = parse(&format!("SELECT shared FROM x {input_kind} y"));
3615 let result =
3616 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3617 let sql = gen(&result);
3618
3619 assert!(
3620 !sql.contains("NATURAL"),
3621 "{input_kind} should expand: {sql}"
3622 );
3623 assert!(sql.contains(output_kind), "join kind should remain: {sql}");
3624 assert!(
3625 sql.contains("ON x.shared = y.shared"),
3626 "join condition should be derived: {sql}"
3627 );
3628 }
3629 }
3630
3631 #[test]
3632 fn test_preserve_natural_join_without_known_common_columns() {
3633 let mut no_common_schema = MappingSchema::new();
3634 no_common_schema
3635 .add_table(
3636 "x",
3637 &[("a".to_string(), DataType::BigInt { length: None })],
3638 None,
3639 )
3640 .expect("schema setup");
3641 no_common_schema
3642 .add_table(
3643 "y",
3644 &[("b".to_string(), DataType::BigInt { length: None })],
3645 None,
3646 )
3647 .expect("schema setup");
3648
3649 let no_common = qualify_columns(
3650 parse("SELECT * FROM x NATURAL JOIN y"),
3651 &no_common_schema,
3652 &QualifyColumnsOptions::new(),
3653 )
3654 .expect("qualify");
3655 assert!(
3656 gen(&no_common).contains("NATURAL JOIN"),
3657 "join without common columns should remain NATURAL"
3658 );
3659
3660 let mut partial_schema = MappingSchema::new();
3661 partial_schema
3662 .add_table(
3663 "x",
3664 &[("a".to_string(), DataType::BigInt { length: None })],
3665 None,
3666 )
3667 .expect("schema setup");
3668 let unknown_right = qualify_columns(
3669 parse("SELECT * FROM x NATURAL JOIN unknown_table"),
3670 &partial_schema,
3671 &QualifyColumnsOptions::new().with_allow_partial(true),
3672 )
3673 .expect("partial qualification");
3674 assert!(
3675 gen(&unknown_right).contains("NATURAL JOIN"),
3676 "join with an unknown schema should remain NATURAL"
3677 );
3678
3679 let unknown_left = qualify_columns(
3680 parse("SELECT * FROM unknown_table NATURAL JOIN x"),
3681 &partial_schema,
3682 &QualifyColumnsOptions::new().with_allow_partial(true),
3683 )
3684 .expect("partial qualification");
3685 assert!(
3686 gen(&unknown_left).contains("NATURAL JOIN"),
3687 "join with an unknown left schema should remain NATURAL"
3688 );
3689
3690 let partially_known_chain = qualify_columns(
3691 parse("SELECT * FROM x NATURAL JOIN unknown_table NATURAL JOIN x AS x2"),
3692 &partial_schema,
3693 &QualifyColumnsOptions::new().with_allow_partial(true),
3694 )
3695 .expect("partial qualification");
3696 assert_eq!(
3697 gen(&partially_known_chain).matches("NATURAL JOIN").count(),
3698 2,
3699 "an unknown earlier source should prevent later inferred join keys"
3700 );
3701 }
3702
3703 #[test]
3704 fn test_qualify_columns_qualified_table_name() {
3705 let expr = parse("SELECT a FROM raw.t1");
3706
3707 let mut schema = MappingSchema::new();
3708 schema
3709 .add_table(
3710 "raw.t1",
3711 &[("a".to_string(), DataType::BigInt { length: None })],
3712 None,
3713 )
3714 .expect("schema setup");
3715
3716 let result =
3717 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3718 let sql = gen(&result);
3719
3720 assert!(
3721 sql.contains("t1.a"),
3722 "column should be qualified with table name: {sql}"
3723 );
3724
3725 let expr = parse("SELECT MAX(a) FROM raw.t1");
3727 let result =
3728 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3729 let sql = gen(&result);
3730 assert!(
3731 sql.contains("t1.a"),
3732 "column in function should be qualified with table name: {sql}"
3733 );
3734
3735 let expr = parse("SELECT ABS(a) FROM raw.t1");
3737 let result =
3738 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3739 let sql = gen(&result);
3740 assert!(
3741 sql.contains("t1.a"),
3742 "column in function should be qualified with table name: {sql}"
3743 );
3744 }
3745
3746 #[test]
3747 fn test_qualify_columns_count_star() {
3748 let expr = parse("SELECT COUNT(*) FROM t1");
3750
3751 let mut schema = MappingSchema::new();
3752 schema
3753 .add_table(
3754 "t1",
3755 &[("id".to_string(), DataType::BigInt { length: None })],
3756 None,
3757 )
3758 .expect("schema setup");
3759
3760 let result =
3761 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3762 let sql = gen(&result);
3763
3764 assert!(
3765 sql.contains("COUNT(*)"),
3766 "COUNT(*) should be preserved: {sql}"
3767 );
3768 }
3769
3770 #[test]
3771 fn test_qualify_columns_correlated_scalar_subquery() {
3772 let expr =
3773 parse("SELECT id, (SELECT AVG(val) FROM t2 WHERE t2.id = t1.id) AS avg_val FROM t1");
3774
3775 let mut schema = MappingSchema::new();
3776 schema
3777 .add_table(
3778 "t1",
3779 &[("id".to_string(), DataType::BigInt { length: None })],
3780 None,
3781 )
3782 .expect("schema setup");
3783 schema
3784 .add_table(
3785 "t2",
3786 &[
3787 ("id".to_string(), DataType::BigInt { length: None }),
3788 ("val".to_string(), DataType::BigInt { length: None }),
3789 ],
3790 None,
3791 )
3792 .expect("schema setup");
3793
3794 let result =
3795 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3796 let sql = gen(&result);
3797
3798 assert!(
3799 sql.contains("t1.id"),
3800 "outer column should be qualified: {sql}"
3801 );
3802 assert!(
3803 sql.contains("t2.id"),
3804 "inner column should be qualified: {sql}"
3805 );
3806 }
3807
3808 #[test]
3809 fn test_qualify_columns_correlated_scalar_subquery_unqualified() {
3810 let expr =
3811 parse("SELECT t1_id, (SELECT AVG(val) FROM t2 WHERE t2_id = t1_id) AS avg_val FROM t1");
3812
3813 let mut schema = MappingSchema::new();
3814 schema
3815 .add_table(
3816 "t1",
3817 &[("t1_id".to_string(), DataType::BigInt { length: None })],
3818 None,
3819 )
3820 .expect("schema setup");
3821 schema
3822 .add_table(
3823 "t2",
3824 &[
3825 ("t2_id".to_string(), DataType::BigInt { length: None }),
3826 ("val".to_string(), DataType::BigInt { length: None }),
3827 ],
3828 None,
3829 )
3830 .expect("schema setup");
3831
3832 let result =
3833 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3834 let sql = gen(&result);
3835
3836 assert!(
3837 sql.contains("t1.t1_id"),
3838 "outer column should be qualified: {sql}"
3839 );
3840 assert!(
3841 sql.contains("t2.t2_id"),
3842 "inner column should be qualified: {sql}"
3843 );
3844 assert!(
3846 sql.contains("= t1.t1_id"),
3847 "correlated column should be qualified: {sql}"
3848 );
3849 }
3850
3851 #[test]
3852 fn test_qualify_columns_correlated_exists_subquery() {
3853 let expr = parse(
3854 "SELECT o_orderpriority FROM orders \
3855 WHERE EXISTS (SELECT * FROM lineitem WHERE l_orderkey = o_orderkey)",
3856 );
3857
3858 let mut schema = MappingSchema::new();
3859 schema
3860 .add_table(
3861 "orders",
3862 &[
3863 ("o_orderpriority".to_string(), DataType::Text),
3864 ("o_orderkey".to_string(), DataType::BigInt { length: None }),
3865 ],
3866 None,
3867 )
3868 .expect("schema setup");
3869 schema
3870 .add_table(
3871 "lineitem",
3872 &[("l_orderkey".to_string(), DataType::BigInt { length: None })],
3873 None,
3874 )
3875 .expect("schema setup");
3876
3877 let result =
3878 qualify_columns(expr, &schema, &QualifyColumnsOptions::new()).expect("qualify");
3879 let sql = gen(&result);
3880
3881 assert!(
3882 sql.contains("orders.o_orderpriority"),
3883 "outer column should be qualified: {sql}"
3884 );
3885 assert!(
3886 sql.contains("lineitem.l_orderkey"),
3887 "inner column should be qualified: {sql}"
3888 );
3889 assert!(
3890 sql.contains("orders.o_orderkey"),
3891 "correlated outer column should be qualified: {sql}"
3892 );
3893 }
3894
3895 #[test]
3896 fn test_qualify_columns_rejects_unknown_table() {
3897 let expr = parse("SELECT id FROM t1 WHERE nonexistent.col = 1");
3898
3899 let mut schema = MappingSchema::new();
3900 schema
3901 .add_table(
3902 "t1",
3903 &[("id".to_string(), DataType::BigInt { length: None })],
3904 None,
3905 )
3906 .expect("schema setup");
3907
3908 let result = qualify_columns(expr, &schema, &QualifyColumnsOptions::new());
3909 assert!(
3910 result.is_err(),
3911 "should reject reference to table not in scope or schema"
3912 );
3913 }
3914
3915 #[test]
3920 fn test_needs_quoting_reserved_word() {
3921 let reserved = get_reserved_words(None);
3922 assert!(needs_quoting("select", &reserved));
3923 assert!(needs_quoting("SELECT", &reserved));
3924 assert!(needs_quoting("from", &reserved));
3925 assert!(needs_quoting("WHERE", &reserved));
3926 assert!(needs_quoting("join", &reserved));
3927 assert!(needs_quoting("table", &reserved));
3928 }
3929
3930 #[test]
3931 fn test_needs_quoting_normal_identifiers() {
3932 let reserved = get_reserved_words(None);
3933 assert!(!needs_quoting("foo", &reserved));
3934 assert!(!needs_quoting("my_column", &reserved));
3935 assert!(!needs_quoting("col1", &reserved));
3936 assert!(!needs_quoting("A", &reserved));
3937 assert!(!needs_quoting("_hidden", &reserved));
3938 }
3939
3940 #[test]
3941 fn test_needs_quoting_special_characters() {
3942 let reserved = get_reserved_words(None);
3943 assert!(needs_quoting("my column", &reserved)); assert!(needs_quoting("my-column", &reserved)); assert!(needs_quoting("my.column", &reserved)); assert!(needs_quoting("col@name", &reserved)); assert!(needs_quoting("col#name", &reserved)); }
3949
3950 #[test]
3951 fn test_needs_quoting_starts_with_digit() {
3952 let reserved = get_reserved_words(None);
3953 assert!(needs_quoting("1col", &reserved));
3954 assert!(needs_quoting("123", &reserved));
3955 assert!(needs_quoting("0_start", &reserved));
3956 }
3957
3958 #[test]
3959 fn test_needs_quoting_empty() {
3960 let reserved = get_reserved_words(None);
3961 assert!(!needs_quoting("", &reserved));
3962 }
3963
3964 #[test]
3965 fn test_maybe_quote_sets_quoted_flag() {
3966 let reserved = get_reserved_words(None);
3967 let mut id = Identifier::new("select");
3968 assert!(!id.quoted);
3969 maybe_quote(&mut id, &reserved);
3970 assert!(id.quoted);
3971 }
3972
3973 #[test]
3974 fn test_maybe_quote_skips_already_quoted() {
3975 let reserved = get_reserved_words(None);
3976 let mut id = Identifier::quoted("myname");
3977 assert!(id.quoted);
3978 maybe_quote(&mut id, &reserved);
3979 assert!(id.quoted); assert_eq!(id.name, "myname"); }
3982
3983 #[test]
3984 fn test_maybe_quote_skips_star() {
3985 let reserved = get_reserved_words(None);
3986 let mut id = Identifier::new("*");
3987 maybe_quote(&mut id, &reserved);
3988 assert!(!id.quoted); }
3990
3991 #[test]
3992 fn test_maybe_quote_skips_normal() {
3993 let reserved = get_reserved_words(None);
3994 let mut id = Identifier::new("normal_col");
3995 maybe_quote(&mut id, &reserved);
3996 assert!(!id.quoted);
3997 }
3998
3999 #[test]
4000 fn test_quote_identifiers_column_with_reserved_name() {
4001 let expr = Expression::boxed_column(Column {
4003 name: Identifier::new("select"),
4004 table: None,
4005 join_mark: false,
4006 trailing_comments: vec![],
4007 span: None,
4008 inferred_type: None,
4009 });
4010 let result = quote_identifiers(expr, None);
4011 if let Expression::Column(col) = &result {
4012 assert!(col.name.quoted, "Column named 'select' should be quoted");
4013 } else {
4014 panic!("Expected Column expression");
4015 }
4016 }
4017
4018 #[test]
4019 fn test_quote_identifiers_column_with_special_chars() {
4020 let expr = Expression::boxed_column(Column {
4021 name: Identifier::new("my column"),
4022 table: None,
4023 join_mark: false,
4024 trailing_comments: vec![],
4025 span: None,
4026 inferred_type: None,
4027 });
4028 let result = quote_identifiers(expr, None);
4029 if let Expression::Column(col) = &result {
4030 assert!(col.name.quoted, "Column with space should be quoted");
4031 } else {
4032 panic!("Expected Column expression");
4033 }
4034 }
4035
4036 #[test]
4037 fn test_quote_identifiers_preserves_normal_column() {
4038 let expr = Expression::boxed_column(Column {
4039 name: Identifier::new("normal_col"),
4040 table: Some(Identifier::new("my_table")),
4041 join_mark: false,
4042 trailing_comments: vec![],
4043 span: None,
4044 inferred_type: None,
4045 });
4046 let result = quote_identifiers(expr, None);
4047 if let Expression::Column(col) = &result {
4048 assert!(!col.name.quoted, "Normal column should not be quoted");
4049 assert!(
4050 !col.table.as_ref().unwrap().quoted,
4051 "Normal table should not be quoted"
4052 );
4053 } else {
4054 panic!("Expected Column expression");
4055 }
4056 }
4057
4058 #[test]
4059 fn test_quote_identifiers_table_ref_reserved() {
4060 let expr = Expression::Table(Box::new(TableRef::new("select")));
4061 let result = quote_identifiers(expr, None);
4062 if let Expression::Table(tr) = &result {
4063 assert!(tr.name.quoted, "Table named 'select' should be quoted");
4064 } else {
4065 panic!("Expected Table expression");
4066 }
4067 }
4068
4069 #[test]
4070 fn test_quote_identifiers_table_ref_schema_and_alias() {
4071 let mut tr = TableRef::new("my_table");
4072 tr.schema = Some(Identifier::new("from"));
4073 tr.alias = Some(Identifier::new("t"));
4074 let expr = Expression::Table(Box::new(tr));
4075 let result = quote_identifiers(expr, None);
4076 if let Expression::Table(tr) = &result {
4077 assert!(!tr.name.quoted, "Normal table name should not be quoted");
4078 assert!(
4079 tr.schema.as_ref().unwrap().quoted,
4080 "Schema named 'from' should be quoted"
4081 );
4082 assert!(
4083 !tr.alias.as_ref().unwrap().quoted,
4084 "Normal alias should not be quoted"
4085 );
4086 } else {
4087 panic!("Expected Table expression");
4088 }
4089 }
4090
4091 #[test]
4092 fn test_quote_identifiers_identifier_node() {
4093 let expr = Expression::Identifier(Identifier::new("order"));
4094 let result = quote_identifiers(expr, None);
4095 if let Expression::Identifier(id) = &result {
4096 assert!(id.quoted, "Identifier named 'order' should be quoted");
4097 } else {
4098 panic!("Expected Identifier expression");
4099 }
4100 }
4101
4102 #[test]
4103 fn test_quote_identifiers_alias() {
4104 let inner = Expression::boxed_column(Column {
4105 name: Identifier::new("val"),
4106 table: None,
4107 join_mark: false,
4108 trailing_comments: vec![],
4109 span: None,
4110 inferred_type: None,
4111 });
4112 let expr = Expression::Alias(Box::new(Alias {
4113 this: inner,
4114 alias: Identifier::new("select"),
4115 column_aliases: vec![Identifier::new("from")],
4116 alias_explicit_as: false,
4117 alias_keyword: None,
4118 pre_alias_comments: vec![],
4119 trailing_comments: vec![],
4120 inferred_type: None,
4121 }));
4122 let result = quote_identifiers(expr, None);
4123 if let Expression::Alias(alias) = &result {
4124 assert!(alias.alias.quoted, "Alias named 'select' should be quoted");
4125 assert!(
4126 alias.column_aliases[0].quoted,
4127 "Column alias named 'from' should be quoted"
4128 );
4129 if let Expression::Column(col) = &alias.this {
4131 assert!(!col.name.quoted);
4132 }
4133 } else {
4134 panic!("Expected Alias expression");
4135 }
4136 }
4137
4138 #[test]
4139 fn test_quote_identifiers_select_recursive() {
4140 let expr = parse("SELECT a, b FROM t WHERE c = 1");
4142 let result = quote_identifiers(expr, None);
4143 let sql = gen(&result);
4145 assert!(sql.contains("a"));
4147 assert!(sql.contains("b"));
4148 assert!(sql.contains("t"));
4149 }
4150
4151 #[test]
4152 fn test_quote_identifiers_digit_start() {
4153 let expr = Expression::boxed_column(Column {
4154 name: Identifier::new("1col"),
4155 table: None,
4156 join_mark: false,
4157 trailing_comments: vec![],
4158 span: None,
4159 inferred_type: None,
4160 });
4161 let result = quote_identifiers(expr, None);
4162 if let Expression::Column(col) = &result {
4163 assert!(
4164 col.name.quoted,
4165 "Column starting with digit should be quoted"
4166 );
4167 } else {
4168 panic!("Expected Column expression");
4169 }
4170 }
4171
4172 #[test]
4173 fn test_quote_identifiers_with_mysql_dialect() {
4174 let reserved = get_reserved_words(Some(DialectType::MySQL));
4175 assert!(needs_quoting("KILL", &reserved));
4177 assert!(needs_quoting("FORCE", &reserved));
4179 }
4180
4181 #[test]
4182 fn test_quote_identifiers_with_postgresql_dialect() {
4183 let reserved = get_reserved_words(Some(DialectType::PostgreSQL));
4184 assert!(needs_quoting("ILIKE", &reserved));
4186 assert!(needs_quoting("VERBOSE", &reserved));
4188 }
4189
4190 #[test]
4191 fn test_quote_identifiers_with_bigquery_dialect() {
4192 let reserved = get_reserved_words(Some(DialectType::BigQuery));
4193 assert!(needs_quoting("STRUCT", &reserved));
4195 assert!(needs_quoting("PROTO", &reserved));
4197 }
4198
4199 #[test]
4200 fn test_quote_identifiers_case_insensitive_reserved() {
4201 let reserved = get_reserved_words(None);
4202 assert!(needs_quoting("Select", &reserved));
4203 assert!(needs_quoting("sElEcT", &reserved));
4204 assert!(needs_quoting("FROM", &reserved));
4205 assert!(needs_quoting("from", &reserved));
4206 }
4207
4208 #[test]
4209 fn test_quote_identifiers_join_using() {
4210 let mut join = crate::expressions::Join {
4212 this: Expression::Table(Box::new(TableRef::new("other"))),
4213 on: None,
4214 using: vec![Identifier::new("key"), Identifier::new("value")],
4215 kind: crate::expressions::JoinKind::Inner,
4216 use_inner_keyword: false,
4217 use_outer_keyword: false,
4218 deferred_condition: false,
4219 join_hint: None,
4220 match_condition: None,
4221 pivots: vec![],
4222 comments: vec![],
4223 nesting_group: 0,
4224 directed: false,
4225 };
4226 let reserved = get_reserved_words(None);
4227 quote_join(&mut join, &reserved);
4228 assert!(
4230 join.using[0].quoted,
4231 "USING identifier 'key' should be quoted"
4232 );
4233 assert!(
4234 !join.using[1].quoted,
4235 "USING identifier 'value' should not be quoted"
4236 );
4237 }
4238
4239 #[test]
4240 fn test_quote_identifiers_cte() {
4241 let mut cte = crate::expressions::Cte {
4243 alias: Identifier::new("select"),
4244 this: Expression::boxed_column(Column {
4245 name: Identifier::new("x"),
4246 table: None,
4247 join_mark: false,
4248 trailing_comments: vec![],
4249 span: None,
4250 inferred_type: None,
4251 }),
4252 columns: vec![Identifier::new("from"), Identifier::new("normal")],
4253 materialized: None,
4254 key_expressions: vec![],
4255 alias_first: false,
4256 comments: Vec::new(),
4257 };
4258 let reserved = get_reserved_words(None);
4259 maybe_quote(&mut cte.alias, &reserved);
4260 for c in &mut cte.columns {
4261 maybe_quote(c, &reserved);
4262 }
4263 assert!(cte.alias.quoted, "CTE alias 'select' should be quoted");
4264 assert!(cte.columns[0].quoted, "CTE column 'from' should be quoted");
4265 assert!(
4266 !cte.columns[1].quoted,
4267 "CTE column 'normal' should not be quoted"
4268 );
4269 }
4270
4271 #[test]
4272 fn test_quote_identifiers_binary_ops_recurse() {
4273 let expr = Expression::Add(Box::new(crate::expressions::BinaryOp::new(
4276 Expression::boxed_column(Column {
4277 name: Identifier::new("select"),
4278 table: None,
4279 join_mark: false,
4280 trailing_comments: vec![],
4281 span: None,
4282 inferred_type: None,
4283 }),
4284 Expression::boxed_column(Column {
4285 name: Identifier::new("normal"),
4286 table: None,
4287 join_mark: false,
4288 trailing_comments: vec![],
4289 span: None,
4290 inferred_type: None,
4291 }),
4292 )));
4293 let result = quote_identifiers(expr, None);
4294 if let Expression::Add(bin) = &result {
4295 if let Expression::Column(left) = &bin.left {
4296 assert!(
4297 left.name.quoted,
4298 "'select' column should be quoted in binary op"
4299 );
4300 }
4301 if let Expression::Column(right) = &bin.right {
4302 assert!(!right.name.quoted, "'normal' column should not be quoted");
4303 }
4304 } else {
4305 panic!("Expected Add expression");
4306 }
4307 }
4308
4309 #[test]
4310 fn test_quote_identifiers_already_quoted_preserved() {
4311 let expr = Expression::boxed_column(Column {
4313 name: Identifier::quoted("normal_name"),
4314 table: None,
4315 join_mark: false,
4316 trailing_comments: vec![],
4317 span: None,
4318 inferred_type: None,
4319 });
4320 let result = quote_identifiers(expr, None);
4321 if let Expression::Column(col) = &result {
4322 assert!(
4323 col.name.quoted,
4324 "Already-quoted identifier should remain quoted"
4325 );
4326 } else {
4327 panic!("Expected Column expression");
4328 }
4329 }
4330
4331 #[test]
4332 fn test_quote_identifiers_full_parsed_query() {
4333 let mut select = crate::expressions::Select::new();
4336 select.expressions.push(Expression::boxed_column(Column {
4337 name: Identifier::new("order"),
4338 table: Some(Identifier::new("t")),
4339 join_mark: false,
4340 trailing_comments: vec![],
4341 span: None,
4342 inferred_type: None,
4343 }));
4344 select.from = Some(crate::expressions::From {
4345 expressions: vec![Expression::Table(Box::new(TableRef::new("t")))],
4346 });
4347 let expr = Expression::Select(Box::new(select));
4348
4349 let result = quote_identifiers(expr, None);
4350 if let Expression::Select(sel) = &result {
4351 if let Expression::Column(col) = &sel.expressions[0] {
4352 assert!(col.name.quoted, "Column named 'order' should be quoted");
4353 assert!(
4354 !col.table.as_ref().unwrap().quoted,
4355 "Table 't' should not be quoted"
4356 );
4357 } else {
4358 panic!("Expected Column in SELECT list");
4359 }
4360 } else {
4361 panic!("Expected Select expression");
4362 }
4363 }
4364
4365 #[test]
4366 fn test_get_reserved_words_all_dialects() {
4367 let dialects = [
4369 None,
4370 Some(DialectType::Generic),
4371 Some(DialectType::MySQL),
4372 Some(DialectType::PostgreSQL),
4373 Some(DialectType::BigQuery),
4374 Some(DialectType::Snowflake),
4375 Some(DialectType::TSQL),
4376 Some(DialectType::ClickHouse),
4377 Some(DialectType::DuckDB),
4378 Some(DialectType::Hive),
4379 Some(DialectType::Spark),
4380 Some(DialectType::Trino),
4381 Some(DialectType::Oracle),
4382 Some(DialectType::Redshift),
4383 ];
4384 for dialect in &dialects {
4385 let words = get_reserved_words(*dialect);
4386 assert!(
4388 words.contains("SELECT"),
4389 "All dialects should have SELECT as reserved"
4390 );
4391 assert!(
4392 words.contains("FROM"),
4393 "All dialects should have FROM as reserved"
4394 );
4395 }
4396 }
4397}