1use std::collections::{HashMap, HashSet};
11
12use crate::builder::engine;
13use crate::expressions::*;
14use crate::traversal::{is_aggregate, ExpressionWalk};
15
16fn xform<F: Fn(Expression) -> Expression>(expr: Expression, fun: F) -> Expression {
19 crate::traversal::transform(expr, &|node| Ok(Some(fun(node))))
20 .unwrap_or_else(|_| Expression::Null(Null))
21}
22
23pub fn add_select_columns(expr: Expression, columns: Vec<Expression>) -> Expression {
32 let mut expression = expr;
33 let _ = engine::append_select(&mut expression, columns, true);
34 expression
35}
36
37pub fn remove_select_columns<F: Fn(&Expression) -> bool>(
39 expr: Expression,
40 predicate: F,
41) -> Expression {
42 if let Expression::Select(mut sel) = expr {
43 sel.expressions.retain(|e| !predicate(e));
44 Expression::Select(sel)
45 } else {
46 expr
47 }
48}
49
50pub fn set_distinct(expr: Expression, distinct: bool) -> Expression {
52 let mut expression = expr;
53 let _ = engine::apply_distinct(&mut expression, distinct);
54 expression
55}
56
57pub fn add_where(expr: Expression, condition: Expression, use_or: bool) -> Expression {
67 if !use_or {
68 if !matches!(expr, Expression::Select(_)) {
69 return expr;
70 }
71 let mut expression = expr;
72 let _ = engine::apply_where(&mut expression, condition, true);
73 return expression;
74 }
75 if let Expression::Select(mut sel) = expr {
76 sel.where_clause = Some(match sel.where_clause.take() {
77 Some(existing) => {
78 let combined = if use_or {
79 Expression::Or(Box::new(BinaryOp::new(existing.this, condition)))
80 } else {
81 Expression::And(Box::new(BinaryOp::new(existing.this, condition)))
82 };
83 Where { this: combined }
84 }
85 None => Where { this: condition },
86 });
87 Expression::Select(sel)
88 } else {
89 expr
90 }
91}
92
93pub fn remove_where(expr: Expression) -> Expression {
95 if let Expression::Select(mut sel) = expr {
96 sel.where_clause = None;
97 Expression::Select(sel)
98 } else {
99 expr
100 }
101}
102
103pub fn set_limit(expr: Expression, limit: usize) -> Expression {
109 set_limit_expr(expr, Expression::number(limit as i64))
110}
111
112pub fn set_limit_expr(expr: Expression, limit: Expression) -> Expression {
114 let mut expression = expr;
115 let _ = engine::apply_limit(&mut expression, limit);
116 expression
117}
118
119pub fn set_offset(expr: Expression, offset: usize) -> Expression {
121 set_offset_expr(expr, Expression::number(offset as i64))
122}
123
124pub fn set_offset_expr(expr: Expression, offset: Expression) -> Expression {
126 let mut expression = expr;
127 let _ = engine::apply_offset(&mut expression, offset);
128 expression
129}
130
131pub fn set_order_by(expr: Expression, expressions: Vec<Expression>) -> Expression {
136 let mut expression = expr;
137 let values = expressions.into_iter().map(engine::ordered).collect();
138 let _ = engine::apply_order_by(&mut expression, values, false);
139 expression
140}
141
142pub fn remove_limit_offset(expr: Expression) -> Expression {
144 if let Expression::Select(mut sel) = expr {
145 sel.limit = None;
146 sel.offset = None;
147 Expression::Select(sel)
148 } else {
149 expr
150 }
151}
152
153pub fn rename_columns(expr: Expression, mapping: &HashMap<String, String>) -> Expression {
162 xform(expr, |node| match node {
163 Expression::Column(mut col) => {
164 if let Some(new_name) = mapping.get(&col.name.name) {
165 col.name.name = new_name.clone();
166 }
167 Expression::Column(col)
168 }
169 other => other,
170 })
171}
172
173#[derive(Debug, Clone)]
175pub struct RenameTablesOptions {
176 pub alias_renamed_tables: bool,
178 pub preserve_existing_aliases: bool,
180}
181
182impl Default for RenameTablesOptions {
183 fn default() -> Self {
184 Self {
185 alias_renamed_tables: false,
186 preserve_existing_aliases: true,
187 }
188 }
189}
190
191impl RenameTablesOptions {
192 pub fn new() -> Self {
193 Self::default()
194 }
195
196 pub fn with_alias_renamed_tables(mut self, alias: bool) -> Self {
197 self.alias_renamed_tables = alias;
198 self
199 }
200
201 pub fn with_preserve_existing_aliases(mut self, preserve: bool) -> Self {
202 self.preserve_existing_aliases = preserve;
203 self
204 }
205}
206
207pub fn rename_tables(expr: Expression, mapping: &HashMap<String, String>) -> Expression {
209 rename_tables_with_options(expr, mapping, &RenameTablesOptions::default())
210}
211
212pub fn rename_tables_with_options(
214 expr: Expression,
215 mapping: &HashMap<String, String>,
216 options: &RenameTablesOptions,
217) -> Expression {
218 xform(expr, |node| match node {
219 Expression::Table(mut tbl) => {
220 if let Some(new_name) = mapping.get(&tbl.name.name) {
221 tbl.name.name = new_name.clone();
222 if options.alias_renamed_tables
223 && (!options.preserve_existing_aliases || tbl.alias.is_none())
224 {
225 tbl.alias = Some(Identifier::new(new_name));
226 tbl.alias_explicit_as = true;
227 }
228 }
229 Expression::Table(tbl)
230 }
231 Expression::Column(mut col) => {
232 if let Some(ref mut table_id) = col.table {
233 if let Some(new_name) = mapping.get(&table_id.name) {
234 table_id.name = new_name.clone();
235 }
236 }
237 Expression::Column(col)
238 }
239 other => other,
240 })
241}
242
243pub fn qualify_columns(expr: Expression, table_name: &str) -> Expression {
247 let table = table_name.to_string();
248 xform(expr, move |node| match node {
249 Expression::Column(mut col) => {
250 if col.table.is_none() {
251 col.table = Some(Identifier::new(&table));
252 }
253 Expression::Column(col)
254 }
255 other => other,
256 })
257}
258
259pub fn replace_nodes<F: Fn(&Expression) -> bool>(
265 expr: Expression,
266 predicate: F,
267 replacement: Expression,
268) -> Expression {
269 xform(expr, |node| {
270 if predicate(&node) {
271 replacement.clone()
272 } else {
273 node
274 }
275 })
276}
277
278pub fn replace_by_type<F, R>(expr: Expression, predicate: F, replacer: R) -> Expression
280where
281 F: Fn(&Expression) -> bool,
282 R: Fn(Expression) -> Expression,
283{
284 xform(expr, |node| {
285 if predicate(&node) {
286 replacer(node)
287 } else {
288 node
289 }
290 })
291}
292
293pub fn remove_nodes<F: Fn(&Expression) -> bool>(expr: Expression, predicate: F) -> Expression {
299 xform(expr, |node| {
300 if predicate(&node) {
301 Expression::Null(Null)
302 } else {
303 node
304 }
305 })
306}
307
308pub fn get_column_names(expr: &Expression) -> Vec<String> {
314 expr.find_all(|e| matches!(e, Expression::Column(_)))
315 .into_iter()
316 .filter_map(|e| {
317 if let Expression::Column(col) = e {
318 Some(col.name.name.clone())
319 } else {
320 None
321 }
322 })
323 .collect()
324}
325
326pub fn get_output_column_names(expr: &Expression) -> Vec<String> {
336 output_column_names_from_query(expr)
337}
338
339pub fn get_output_column_names_for_dialect(
346 expr: &Expression,
347 dialect: Option<crate::dialects::DialectType>,
348) -> Vec<String> {
349 crate::set_operation::query_output_identifiers(expr, dialect)
350 .map(|identifiers| {
351 identifiers
352 .into_iter()
353 .map(|identifier| identifier.name)
354 .collect()
355 })
356 .unwrap_or_else(|_| output_column_names_from_query(expr))
357}
358
359fn output_column_names_from_query(expr: &Expression) -> Vec<String> {
360 match expr {
361 Expression::Select(select) => select_output_column_names(select),
362 Expression::Union(union) => output_column_names_from_query(&union.left),
363 Expression::Intersect(intersect) => output_column_names_from_query(&intersect.left),
364 Expression::Except(except) => output_column_names_from_query(&except.left),
365 Expression::Subquery(subquery) => output_column_names_from_query(&subquery.this),
366 _ => Vec::new(),
367 }
368}
369
370fn select_output_column_names(select: &Select) -> Vec<String> {
371 let mut names = Vec::new();
372 for expr in &select.expressions {
373 if let Some(name) = expression_output_name(expr) {
374 names.push(name);
375 }
376 }
377 names
378}
379
380fn expression_output_name(expr: &Expression) -> Option<String> {
381 match expr {
382 Expression::Alias(alias) => Some(alias.alias.name.clone()),
383 Expression::Column(col) => Some(col.name.name.clone()),
384 Expression::Star(_) => Some("*".to_string()),
385 Expression::Identifier(id) => Some(id.name.clone()),
386 Expression::Aliases(aliases) => aliases.expressions.iter().find_map(|e| match e {
387 Expression::Identifier(id) => Some(id.name.clone()),
388 _ => None,
389 }),
390 _ => None,
391 }
392}
393
394pub fn get_table_names(expr: &Expression) -> Vec<String> {
396 fn collect_cte_aliases(with_clause: &With, aliases: &mut HashSet<String>) {
397 for cte in &with_clause.ctes {
398 aliases.insert(cte.alias.name.clone());
399 }
400 }
401
402 fn push_table_ref_name(
403 table: &TableRef,
404 cte_aliases: &HashSet<String>,
405 names: &mut Vec<String>,
406 ) {
407 let name = table.name.name.clone();
408 if !name.is_empty() && !cte_aliases.contains(&name) {
409 names.push(name);
410 }
411 }
412
413 let mut cte_aliases: HashSet<String> = HashSet::new();
414 for node in expr.dfs() {
415 match node {
416 Expression::Select(select) => {
417 if let Some(with) = &select.with {
418 collect_cte_aliases(with, &mut cte_aliases);
419 }
420 }
421 Expression::Insert(insert) => {
422 if let Some(with) = &insert.with {
423 collect_cte_aliases(with, &mut cte_aliases);
424 }
425 }
426 Expression::Update(update) => {
427 if let Some(with) = &update.with {
428 collect_cte_aliases(with, &mut cte_aliases);
429 }
430 }
431 Expression::Delete(delete) => {
432 if let Some(with) = &delete.with {
433 collect_cte_aliases(with, &mut cte_aliases);
434 }
435 }
436 Expression::Union(union) => {
437 if let Some(with) = &union.with {
438 collect_cte_aliases(with, &mut cte_aliases);
439 }
440 }
441 Expression::Intersect(intersect) => {
442 if let Some(with) = &intersect.with {
443 collect_cte_aliases(with, &mut cte_aliases);
444 }
445 }
446 Expression::Except(except) => {
447 if let Some(with) = &except.with {
448 collect_cte_aliases(with, &mut cte_aliases);
449 }
450 }
451 Expression::CreateTable(create) => {
452 if let Some(with) = &create.with_cte {
453 collect_cte_aliases(with, &mut cte_aliases);
454 }
455 }
456 Expression::Merge(merge) => {
457 if let Some(with_) = &merge.with_ {
458 if let Expression::With(with_clause) = with_.as_ref() {
459 collect_cte_aliases(with_clause, &mut cte_aliases);
460 }
461 }
462 }
463 _ => {}
464 }
465 }
466
467 let mut names = Vec::new();
468 for node in expr.dfs() {
469 match node {
470 Expression::Table(tbl) => {
471 let name = tbl.name.name.clone();
472 if !name.is_empty() && !cte_aliases.contains(&name) {
473 names.push(name);
474 }
475 }
476 Expression::Insert(insert) => {
477 push_table_ref_name(&insert.table, &cte_aliases, &mut names);
478 }
479 Expression::Update(update) => {
480 push_table_ref_name(&update.table, &cte_aliases, &mut names);
481 for table in &update.extra_tables {
482 push_table_ref_name(table, &cte_aliases, &mut names);
483 }
484 }
485 Expression::Delete(delete) => {
486 push_table_ref_name(&delete.table, &cte_aliases, &mut names);
487 for table in &delete.using {
488 push_table_ref_name(table, &cte_aliases, &mut names);
489 }
490 for table in &delete.tables {
491 push_table_ref_name(table, &cte_aliases, &mut names);
492 }
493 }
494 Expression::CreateTable(create) => {
495 push_table_ref_name(&create.name, &cte_aliases, &mut names);
496 if let Some(as_select) = &create.as_select {
497 names.extend(get_table_names(as_select));
498 }
499 if let Some(with) = &create.with_cte {
500 for cte in &with.ctes {
501 names.extend(get_table_names(&cte.this));
502 }
503 }
504 }
505 Expression::Cache(cache) => {
506 let name = cache.table.name.clone();
507 if !name.is_empty() && !cte_aliases.contains(&name) {
508 names.push(name);
509 }
510 }
511 Expression::Uncache(uncache) => {
512 let name = uncache.table.name.clone();
513 if !name.is_empty() && !cte_aliases.contains(&name) {
514 names.push(name);
515 }
516 }
517 Expression::CreateSynonym(synonym) => {
518 push_table_ref_name(&synonym.name, &cte_aliases, &mut names);
519 push_table_ref_name(&synonym.target, &cte_aliases, &mut names);
520 }
521 _ => {}
522 }
523 }
524
525 names
526}
527
528pub fn get_identifiers(expr: &Expression) -> Vec<&Expression> {
530 expr.find_all(|e| matches!(e, Expression::Identifier(_)))
531}
532
533pub fn get_functions(expr: &Expression) -> Vec<&Expression> {
535 expr.find_all(|e| {
536 matches!(
537 e,
538 Expression::Function(_) | Expression::AggregateFunction(_)
539 )
540 })
541}
542
543pub fn get_literals(expr: &Expression) -> Vec<&Expression> {
545 expr.find_all(|e| {
546 matches!(
547 e,
548 Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_)
549 )
550 })
551}
552
553pub fn get_subqueries(expr: &Expression) -> Vec<&Expression> {
555 expr.find_all(|e| matches!(e, Expression::Subquery(_)))
556}
557
558pub fn get_aggregate_functions(expr: &Expression) -> Vec<&Expression> {
563 expr.find_all(is_aggregate)
564}
565
566pub fn get_window_functions(expr: &Expression) -> Vec<&Expression> {
568 expr.find_all(|e| matches!(e, Expression::WindowFunction(_)))
569}
570
571pub fn node_count(expr: &Expression) -> usize {
573 expr.dfs().count()
574}
575
576#[cfg(test)]
577mod tests {
578 use super::*;
579 use crate::parser::Parser;
580
581 fn parse_one(sql: &str) -> Expression {
582 let mut exprs = Parser::parse_sql(sql).unwrap();
583 exprs.remove(0)
584 }
585
586 #[test]
587 fn test_add_where() {
588 let expr = parse_one("SELECT a FROM t");
589 let cond = Expression::Eq(Box::new(BinaryOp::new(
590 Expression::column("b"),
591 Expression::number(1),
592 )));
593 let result = add_where(expr, cond, false);
594 let sql = result.sql();
595 assert!(sql.contains("WHERE"), "Expected WHERE in: {}", sql);
596 assert!(sql.contains("b = 1"), "Expected condition in: {}", sql);
597 }
598
599 #[test]
600 fn test_add_where_combines_with_and() {
601 let expr = parse_one("SELECT a FROM t WHERE x = 1");
602 let cond = Expression::Eq(Box::new(BinaryOp::new(
603 Expression::column("y"),
604 Expression::number(2),
605 )));
606 let result = add_where(expr, cond, false);
607 let sql = result.sql();
608 assert!(sql.contains("AND"), "Expected AND in: {}", sql);
609 }
610
611 #[test]
612 fn test_remove_where() {
613 let expr = parse_one("SELECT a FROM t WHERE x = 1");
614 let result = remove_where(expr);
615 let sql = result.sql();
616 assert!(!sql.contains("WHERE"), "Should not contain WHERE: {}", sql);
617 }
618
619 #[test]
620 fn test_set_limit() {
621 let expr = parse_one("SELECT a FROM t");
622 let result = set_limit(expr, 10);
623 let sql = result.sql();
624 assert!(sql.contains("LIMIT 10"), "Expected LIMIT in: {}", sql);
625 }
626
627 #[test]
628 fn test_set_limit_on_set_operation() {
629 let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
630 let result = set_limit(expr, 10);
631 let sql = result.sql();
632 assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u LIMIT 10");
633 }
634
635 #[test]
636 fn test_set_offset() {
637 let expr = parse_one("SELECT a FROM t");
638 let result = set_offset(expr, 5);
639 let sql = result.sql();
640 assert!(sql.contains("OFFSET 5"), "Expected OFFSET in: {}", sql);
641 }
642
643 #[test]
644 fn test_set_offset_on_set_operation() {
645 let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
646 let result = set_offset(expr, 5);
647 let sql = result.sql();
648 assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u OFFSET 5");
649 }
650
651 #[test]
652 fn test_set_order_by_on_set_operation() {
653 let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
654 let result = set_order_by(expr, vec![Expression::column("a")]);
655 let sql = result.sql();
656 assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u ORDER BY a");
657 }
658
659 #[test]
660 fn test_remove_limit_offset() {
661 let expr = parse_one("SELECT a FROM t LIMIT 10 OFFSET 5");
662 let result = remove_limit_offset(expr);
663 let sql = result.sql();
664 assert!(!sql.contains("LIMIT"), "Should not contain LIMIT: {}", sql);
665 assert!(
666 !sql.contains("OFFSET"),
667 "Should not contain OFFSET: {}",
668 sql
669 );
670 }
671
672 #[test]
673 fn test_get_column_names() {
674 let expr = parse_one("SELECT a, b, c FROM t");
675 let names = get_column_names(&expr);
676 assert!(names.contains(&"a".to_string()));
677 assert!(names.contains(&"b".to_string()));
678 assert!(names.contains(&"c".to_string()));
679 }
680
681 #[test]
682 fn test_get_output_column_names_select() {
683 let expr = parse_one("SELECT a, b AS c, 1 FROM t");
684 let names = get_output_column_names(&expr);
685 assert_eq!(names, vec!["a".to_string(), "c".to_string()]);
686 }
687
688 #[test]
689 fn test_get_output_column_names_union_left_projection() {
690 let expr =
691 parse_one("SELECT id, name FROM customers UNION ALL SELECT id, name FROM employees");
692 let names = get_output_column_names(&expr);
693 assert_eq!(names, vec!["id".to_string(), "name".to_string()]);
694 }
695
696 #[test]
697 fn test_get_output_column_names_union_uses_left_aliases() {
698 let expr = parse_one("SELECT id AS c1, name AS c2 FROM t1 UNION SELECT x, y FROM t2");
699 let names = get_output_column_names(&expr);
700 assert_eq!(names, vec!["c1".to_string(), "c2".to_string()]);
701 }
702
703 #[test]
704 fn test_get_output_column_names_uses_dialect_by_name_layout() {
705 for dialect in [
706 crate::dialects::DialectType::DuckDB,
707 crate::dialects::DialectType::Snowflake,
708 ] {
709 let expr = crate::parse_one(
710 "SELECT 1 AS left_value UNION ALL BY NAME SELECT 2 AS right_value",
711 dialect,
712 )
713 .expect("parse");
714 assert_eq!(
715 get_output_column_names_for_dialect(&expr, Some(dialect)),
716 vec!["left_value", "right_value"]
717 );
718 }
719
720 let expr = crate::parse_one(
721 "SELECT 1 AS a, 2 AS b UNION ALL BY NAME SELECT 3 AS b, 4 AS a",
722 crate::dialects::DialectType::BigQuery,
723 )
724 .expect("parse");
725 assert_eq!(
726 get_output_column_names_for_dialect(
727 &expr,
728 Some(crate::dialects::DialectType::BigQuery),
729 ),
730 vec!["a", "b"]
731 );
732 }
733
734 #[test]
735 fn test_get_column_names_union_still_returns_all_references() {
736 let expr =
737 parse_one("SELECT id, name FROM customers UNION ALL SELECT id, name FROM employees");
738 let names = get_column_names(&expr);
739 assert_eq!(
740 names,
741 vec![
742 "id".to_string(),
743 "name".to_string(),
744 "id".to_string(),
745 "name".to_string()
746 ]
747 );
748 }
749
750 #[test]
751 fn test_get_table_names() {
752 let expr = parse_one("SELECT a FROM users");
753 let names = get_table_names(&expr);
754 assert_eq!(names, vec!["users".to_string()]);
755 }
756
757 #[test]
758 fn test_get_table_names_excludes_cte_aliases() {
759 let expr = parse_one(
760 "WITH cte AS (SELECT * FROM users) SELECT * FROM cte JOIN orders o ON cte.id = o.id",
761 );
762 let names = get_table_names(&expr);
763 assert!(names.iter().any(|n| n == "users"));
764 assert!(names.iter().any(|n| n == "orders"));
765 assert!(!names.iter().any(|n| n == "cte"));
766 }
767
768 #[test]
769 fn test_get_table_names_includes_dml_targets() {
770 let insert_expr = parse_one("INSERT INTO users (id) VALUES (1)");
771 let insert_names = get_table_names(&insert_expr);
772 assert!(insert_names.iter().any(|n| n == "users"));
773
774 let update_expr =
775 parse_one("UPDATE users SET name = 'x' FROM accounts WHERE users.id = accounts.id");
776 let update_names = get_table_names(&update_expr);
777 assert!(update_names.iter().any(|n| n == "users"));
778 assert!(update_names.iter().any(|n| n == "accounts"));
779
780 let delete_expr =
781 parse_one("DELETE FROM users USING accounts WHERE users.id = accounts.id");
782 let delete_names = get_table_names(&delete_expr);
783 assert!(delete_names.iter().any(|n| n == "users"));
784 assert!(delete_names.iter().any(|n| n == "accounts"));
785
786 let create_expr = parse_one("CREATE TABLE out_table AS SELECT 1 AS id FROM src");
787 let create_names = get_table_names(&create_expr);
788 assert!(create_names.iter().any(|n| n == "out_table"));
789 assert!(create_names.iter().any(|n| n == "src"));
790 }
791
792 #[test]
793 fn test_node_count() {
794 let expr = parse_one("SELECT a FROM t");
795 let count = node_count(&expr);
796 assert!(count > 0, "Expected non-zero node count");
797 }
798
799 #[test]
800 fn test_rename_columns() {
801 let expr = parse_one("SELECT old_name FROM t");
802 let mut mapping = HashMap::new();
803 mapping.insert("old_name".to_string(), "new_name".to_string());
804 let result = rename_columns(expr, &mapping);
805 let sql = result.sql();
806 assert!(sql.contains("new_name"), "Expected new_name in: {}", sql);
807 assert!(
808 !sql.contains("old_name"),
809 "Should not contain old_name: {}",
810 sql
811 );
812 }
813
814 #[test]
815 fn test_rename_tables() {
816 let expr = parse_one("SELECT a FROM old_table");
817 let mut mapping = HashMap::new();
818 mapping.insert("old_table".to_string(), "new_table".to_string());
819 let result = rename_tables(expr, &mapping);
820 let sql = result.sql();
821 assert!(sql.contains("new_table"), "Expected new_table in: {}", sql);
822 }
823
824 #[test]
825 fn test_rename_tables_with_alias_renamed_tables() {
826 let expr = parse_one("SELECT a FROM old_table");
827 let mut mapping = HashMap::new();
828 mapping.insert("old_table".to_string(), "new_table".to_string());
829 let options = RenameTablesOptions::new().with_alias_renamed_tables(true);
830 let result = rename_tables_with_options(expr, &mapping, &options);
831 let sql = result.sql();
832
833 assert_eq!(sql, "SELECT a FROM new_table AS new_table");
834 }
835
836 #[test]
837 fn test_rename_tables_with_alias_preserves_existing_alias() {
838 let expr = parse_one("SELECT a FROM old_table AS t");
839 let mut mapping = HashMap::new();
840 mapping.insert("old_table".to_string(), "new_table".to_string());
841 let options = RenameTablesOptions::new().with_alias_renamed_tables(true);
842 let result = rename_tables_with_options(expr, &mapping, &options);
843 let sql = result.sql();
844
845 assert_eq!(sql, "SELECT a FROM new_table AS t");
846 }
847
848 #[test]
849 fn test_set_distinct() {
850 let expr = parse_one("SELECT a FROM t");
851 let result = set_distinct(expr, true);
852 let sql = result.sql();
853 assert!(sql.contains("DISTINCT"), "Expected DISTINCT in: {}", sql);
854 }
855
856 #[test]
857 fn test_add_select_columns() {
858 let expr = parse_one("SELECT a FROM t");
859 let result = add_select_columns(expr, vec![Expression::column("b")]);
860 let sql = result.sql();
861 assert!(
862 sql.contains("a, b") || sql.contains("a,b"),
863 "Expected a, b in: {}",
864 sql
865 );
866 }
867
868 #[test]
869 fn test_qualify_columns() {
870 let expr = parse_one("SELECT a, b FROM t");
871 let result = qualify_columns(expr, "t");
872 let sql = result.sql();
873 assert!(sql.contains("t.a"), "Expected t.a in: {}", sql);
874 assert!(sql.contains("t.b"), "Expected t.b in: {}", sql);
875 }
876
877 #[test]
878 fn test_get_functions() {
879 let expr = parse_one("SELECT COUNT(*), UPPER(name) FROM t");
880 let funcs = get_functions(&expr);
881 let _ = funcs.len();
886 }
887
888 #[test]
889 fn test_get_aggregate_functions() {
890 let expr = crate::parse_one(
891 "SELECT COUNT_IF(numeric_value > 0), MEDIAN(numeric_value), FIRST(numeric_value) FROM source_table",
892 crate::dialects::DialectType::DuckDB,
893 )
894 .unwrap();
895 let aggs = get_aggregate_functions(&expr);
896 let aggregate_types: Vec<_> = aggs.iter().map(|agg| agg.variant_name()).collect();
897
898 assert_eq!(aggregate_types, vec!["count_if", "median", "first"]);
899 }
900}