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