Skip to main content

sqlglot_rust/generator/
sql_generator.rs

1use crate::ast::*;
2use crate::dialects::Dialect;
3
4/// SQL code generator that converts an AST into a SQL string.
5///
6/// Supports all statement and expression types defined in the AST,
7/// including CTEs, subqueries, UNION/INTERSECT/EXCEPT, CAST, window
8/// functions, EXISTS, EXTRACT, INTERVAL, and more.
9pub struct Generator {
10    output: String,
11    /// When true, emit formatted SQL with indentation and newlines.
12    pretty: bool,
13    indent: usize,
14    /// Target dialect for dialect-specific generation (typed functions).
15    dialect: Option<Dialect>,
16}
17
18impl Generator {
19    #[must_use]
20    pub fn new() -> Self {
21        Self {
22            output: String::new(),
23            pretty: false,
24            indent: 0,
25            dialect: None,
26        }
27    }
28
29    /// Create a generator that produces formatted SQL.
30    #[must_use]
31    pub fn pretty() -> Self {
32        Self {
33            output: String::new(),
34            pretty: true,
35            indent: 0,
36            dialect: None,
37        }
38    }
39
40    /// Create a generator targeting a specific dialect.
41    #[must_use]
42    pub fn with_dialect(dialect: Dialect) -> Self {
43        Self {
44            output: String::new(),
45            pretty: false,
46            indent: 0,
47            dialect: Some(dialect),
48        }
49    }
50
51    /// Generate SQL from a statement.
52    #[must_use]
53    pub fn generate(&mut self, statement: &Statement) -> String {
54        self.output.clear();
55        self.gen_statement(statement);
56        self.output.clone()
57    }
58
59    /// Generate SQL for an expression (static helper for `Expr::sql()`).
60    #[must_use]
61    pub fn expr_to_sql(expr: &Expr) -> String {
62        let mut g = Self::new();
63        g.gen_expr(expr);
64        g.output
65    }
66
67    fn write(&mut self, s: &str) {
68        self.output.push_str(s);
69    }
70
71    /// Emit a newline followed by current indentation (pretty mode only).
72    fn newline(&mut self) {
73        if self.pretty {
74            self.output.push('\n');
75            for _ in 0..self.indent {
76                self.output.push_str("  ");
77            }
78        }
79    }
80
81    /// In pretty mode: newline + indent. In compact mode: a single space.
82    fn sep(&mut self) {
83        if self.pretty {
84            self.newline();
85        } else {
86            self.output.push(' ');
87        }
88    }
89
90    fn indent_up(&mut self) {
91        self.indent += 1;
92    }
93
94    fn indent_down(&mut self) {
95        self.indent = self.indent.saturating_sub(1);
96    }
97
98    fn write_keyword(&mut self, s: &str) {
99        self.write(s);
100    }
101
102    /// Write an identifier with the given quoting style.
103    /// If a target dialect is set and the identifier is quoted, the quoting
104    /// is transformed to the target dialect's canonical style.
105    fn write_quoted(&mut self, name: &str, style: QuoteStyle) {
106        let effective_style = if style.is_quoted() {
107            if let Some(dialect) = self.dialect {
108                QuoteStyle::for_dialect(dialect)
109            } else {
110                style
111            }
112        } else {
113            style
114        };
115        match effective_style {
116            QuoteStyle::None => self.write(name),
117            QuoteStyle::DoubleQuote => {
118                self.write("\"");
119                self.write(&name.replace('"', "\"\""));
120                self.write("\"");
121            }
122            QuoteStyle::Backtick => {
123                self.write("`");
124                self.write(&name.replace('`', "``"));
125                self.write("`");
126            }
127            QuoteStyle::Bracket => {
128                self.write("[");
129                self.write(&name.replace(']', "]]"));
130                self.write("]");
131            }
132        }
133    }
134
135    // ══════════════════════════════════════════════════════════════
136    // Statements
137    // ══════════════════════════════════════════════════════════════
138
139    /// Emit comment strings, each on its own line.
140    /// MySQL hash comments (`#`) are converted to standard `--` comments
141    /// when the target dialect is not MySQL.
142    fn gen_comments(&mut self, comments: &[String]) {
143        for comment in comments {
144            let normalized = self.normalize_comment(comment);
145            self.write(&normalized);
146            self.newline_or_space();
147        }
148    }
149
150    /// Normalize a comment for the target dialect.
151    /// Converts MySQL `#` comments to `--` when targeting non-MySQL dialects.
152    fn normalize_comment(&self, comment: &str) -> String {
153        if comment.starts_with('#') {
154            let is_mysql_target = matches!(
155                self.dialect,
156                Some(Dialect::Mysql | Dialect::Doris | Dialect::SingleStore | Dialect::StarRocks)
157            );
158            if !is_mysql_target {
159                return format!("--{}", &comment[1..]);
160            }
161        }
162        comment.to_string()
163    }
164
165    /// Emit a newline in pretty mode or a space in compact mode.
166    fn newline_or_space(&mut self) {
167        if self.pretty {
168            self.output.push('\n');
169            for _ in 0..self.indent {
170                self.output.push_str("  ");
171            }
172        } else {
173            self.output.push('\n');
174        }
175    }
176
177    fn gen_statement(&mut self, statement: &Statement) {
178        // Emit leading comments for statements that carry them.
179        match statement {
180            Statement::Select(s) => {
181                self.gen_comments(&s.comments);
182                self.gen_select(s);
183            }
184            Statement::Insert(s) => {
185                self.gen_comments(&s.comments);
186                self.gen_insert(s);
187            }
188            Statement::Update(s) => {
189                self.gen_comments(&s.comments);
190                self.gen_update(s);
191            }
192            Statement::Delete(s) => {
193                self.gen_comments(&s.comments);
194                self.gen_delete(s);
195            }
196            Statement::CreateTable(s) => {
197                self.gen_comments(&s.comments);
198                self.gen_create_table(s);
199            }
200            Statement::DropTable(s) => {
201                self.gen_comments(&s.comments);
202                self.gen_drop_table(s);
203            }
204            Statement::SetOperation(s) => {
205                self.gen_comments(&s.comments);
206                self.gen_set_operation(s);
207            }
208            Statement::AlterTable(s) => {
209                self.gen_comments(&s.comments);
210                self.gen_alter_table(s);
211            }
212            Statement::CreateView(s) => {
213                self.gen_comments(&s.comments);
214                self.gen_create_view(s);
215            }
216            Statement::DropView(s) => {
217                self.gen_comments(&s.comments);
218                self.gen_drop_view(s);
219            }
220            Statement::Truncate(s) => {
221                self.gen_comments(&s.comments);
222                self.gen_truncate(s);
223            }
224            Statement::Transaction(s) => self.gen_transaction(s),
225            Statement::Explain(s) => {
226                self.gen_comments(&s.comments);
227                self.gen_explain(s);
228            }
229            Statement::Use(s) => {
230                self.gen_comments(&s.comments);
231                self.gen_use(s);
232            }
233            Statement::Merge(s) => {
234                self.gen_comments(&s.comments);
235                self.gen_merge(s);
236            }
237            Statement::Expression(e) => self.gen_expr(e),
238        }
239    }
240
241    // ── SELECT ──────────────────────────────────────────────────
242
243    fn gen_select(&mut self, sel: &SelectStatement) {
244        // CTEs
245        if !sel.ctes.is_empty() {
246            self.gen_ctes(&sel.ctes);
247            self.sep();
248        }
249
250        self.write_keyword("SELECT");
251        if sel.distinct {
252            self.write(" ");
253            self.write_keyword("DISTINCT");
254        }
255        if let Some(top) = &sel.top {
256            self.write(" ");
257            self.write_keyword("TOP ");
258            self.gen_expr(top);
259        }
260
261        // columns
262        if self.pretty {
263            self.indent_up();
264            for (i, item) in sel.columns.iter().enumerate() {
265                self.newline();
266                self.gen_select_item(item);
267                if i < sel.columns.len() - 1 {
268                    self.write(",");
269                }
270            }
271            self.indent_down();
272        } else {
273            self.write(" ");
274            for (i, item) in sel.columns.iter().enumerate() {
275                if i > 0 {
276                    self.write(", ");
277                }
278                self.gen_select_item(item);
279            }
280        }
281
282        if let Some(from) = &sel.from {
283            self.sep();
284            self.write_keyword("FROM");
285            if self.pretty {
286                self.indent_up();
287                self.newline();
288                self.gen_table_source(&from.source);
289                self.indent_down();
290            } else {
291                self.write(" ");
292                self.gen_table_source(&from.source);
293            }
294        }
295
296        for join in &sel.joins {
297            self.gen_join(join);
298        }
299
300        if let Some(wh) = &sel.where_clause {
301            self.sep();
302            self.write_keyword("WHERE");
303            if self.pretty {
304                self.indent_up();
305                self.newline();
306                self.gen_expr(wh);
307                self.indent_down();
308            } else {
309                self.write(" ");
310                self.gen_expr(wh);
311            }
312        }
313
314        if !sel.group_by.is_empty() {
315            self.sep();
316            self.write_keyword("GROUP BY");
317            if self.pretty {
318                self.indent_up();
319                self.newline();
320                self.gen_expr_list(&sel.group_by);
321                self.indent_down();
322            } else {
323                self.write(" ");
324                self.gen_expr_list(&sel.group_by);
325            }
326        }
327
328        if let Some(having) = &sel.having {
329            self.sep();
330            self.write_keyword("HAVING");
331            if self.pretty {
332                self.indent_up();
333                self.newline();
334                self.gen_expr(having);
335                self.indent_down();
336            } else {
337                self.write(" ");
338                self.gen_expr(having);
339            }
340        }
341
342        if let Some(qualify) = &sel.qualify {
343            self.sep();
344            self.write_keyword("QUALIFY");
345            if self.pretty {
346                self.indent_up();
347                self.newline();
348                self.gen_expr(qualify);
349                self.indent_down();
350            } else {
351                self.write(" ");
352                self.gen_expr(qualify);
353            }
354        }
355
356        if !sel.window_definitions.is_empty() {
357            self.sep();
358            self.write_keyword("WINDOW ");
359            for (i, wd) in sel.window_definitions.iter().enumerate() {
360                if i > 0 {
361                    self.write(", ");
362                }
363                self.write(&wd.name);
364                self.write(" AS (");
365                self.gen_window_spec(&wd.spec);
366                self.write(")");
367            }
368        }
369
370        self.gen_order_by(&sel.order_by);
371
372        if let Some(limit) = &sel.limit {
373            self.sep();
374            self.write_keyword("LIMIT ");
375            self.gen_expr(limit);
376        }
377
378        if let Some(offset) = &sel.offset {
379            self.sep();
380            if matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric)) {
381                self.write_keyword("OFFSET ");
382                self.gen_expr(offset);
383                self.write(" ");
384                self.write_keyword("ROWS");
385            } else {
386                self.write_keyword("OFFSET ");
387                self.gen_expr(offset);
388            }
389        }
390
391        if let Some(fetch) = &sel.fetch_first {
392            self.sep();
393            if matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric)) {
394                self.write_keyword("FETCH NEXT ");
395                self.gen_expr(fetch);
396                self.write(" ");
397                self.write_keyword("ROWS ONLY");
398            } else {
399                self.write_keyword("FETCH FIRST ");
400                self.gen_expr(fetch);
401                self.write(" ");
402                self.write_keyword("ROWS ONLY");
403            }
404        }
405    }
406
407    fn gen_ctes(&mut self, ctes: &[Cte]) {
408        self.write_keyword("WITH ");
409        if ctes.iter().any(|c| c.recursive) {
410            self.write_keyword("RECURSIVE ");
411        }
412        for (i, cte) in ctes.iter().enumerate() {
413            if i > 0 {
414                self.write(",");
415                self.sep();
416            }
417            self.write_quoted(&cte.name, cte.name_quote_style);
418            if !cte.columns.is_empty() {
419                self.write("(");
420                self.write(&cte.columns.join(", "));
421                self.write(")");
422            }
423            self.write(" ");
424            self.write_keyword("AS ");
425            if let Some(true) = cte.materialized {
426                self.write_keyword("MATERIALIZED ");
427            } else if let Some(false) = cte.materialized {
428                self.write_keyword("NOT MATERIALIZED ");
429            }
430            self.write("(");
431            if self.pretty {
432                self.indent_up();
433                self.newline();
434                self.gen_statement(&cte.query);
435                self.indent_down();
436                self.newline();
437            } else {
438                self.gen_statement(&cte.query);
439            }
440            self.write(")");
441        }
442    }
443
444    fn gen_select_item(&mut self, item: &SelectItem) {
445        match item {
446            SelectItem::Wildcard => self.write("*"),
447            SelectItem::QualifiedWildcard { table } => {
448                self.write(table);
449                self.write(".*");
450            }
451            SelectItem::Expr {
452                expr,
453                alias,
454                alias_quote_style,
455            } => {
456                self.gen_expr(expr);
457                if let Some(alias) = alias {
458                    self.write(" ");
459                    self.write_keyword("AS ");
460                    self.write_quoted(alias, *alias_quote_style);
461                }
462            }
463        }
464    }
465
466    fn gen_table_source(&mut self, source: &TableSource) {
467        match source {
468            TableSource::Table(table_ref) => self.gen_table_ref(table_ref),
469            TableSource::Subquery {
470                query,
471                alias,
472                alias_quote_style,
473            } => {
474                self.write("(");
475                self.gen_statement(query);
476                self.write(")");
477                if let Some(alias) = alias {
478                    self.write(" ");
479                    if !self.omit_table_alias_as() {
480                        self.write_keyword("AS ");
481                    }
482                    self.write_quoted(alias, *alias_quote_style);
483                }
484            }
485            TableSource::TableFunction {
486                name,
487                args,
488                alias,
489                alias_quote_style,
490            } => {
491                self.write(name);
492                self.write("(");
493                self.gen_expr_list(args);
494                self.write(")");
495                if let Some(alias) = alias {
496                    self.write(" ");
497                    if !self.omit_table_alias_as() {
498                        self.write_keyword("AS ");
499                    }
500                    self.write_quoted(alias, *alias_quote_style);
501                }
502            }
503            TableSource::Lateral { source } => {
504                self.write_keyword("LATERAL ");
505                self.gen_table_source(source);
506            }
507            TableSource::Unnest {
508                expr,
509                alias,
510                alias_quote_style,
511                with_offset,
512            } => {
513                self.write_keyword("UNNEST(");
514                self.gen_expr(expr);
515                self.write(")");
516                if let Some(alias) = alias {
517                    self.write(" ");
518                    if !self.omit_table_alias_as() {
519                        self.write_keyword("AS ");
520                    }
521                    self.write_quoted(alias, *alias_quote_style);
522                }
523                if *with_offset {
524                    self.write(" ");
525                    self.write_keyword("WITH OFFSET");
526                }
527            }
528            TableSource::Pivot {
529                source,
530                aggregate,
531                for_column,
532                in_values,
533                alias,
534                alias_quote_style,
535            } => {
536                self.gen_table_source(source);
537                self.write(" ");
538                self.write_keyword("PIVOT");
539                self.write(" (");
540                self.gen_expr(aggregate);
541                self.write(" ");
542                self.write_keyword("FOR ");
543                self.write(for_column);
544                self.write(" ");
545                self.write_keyword("IN ");
546                self.write("(");
547                self.gen_pivot_values(in_values);
548                self.write("))");
549                if let Some(alias) = alias {
550                    self.write(" ");
551                    if !self.omit_table_alias_as() {
552                        self.write_keyword("AS ");
553                    }
554                    self.write_quoted(alias, *alias_quote_style);
555                }
556            }
557            TableSource::Unpivot {
558                source,
559                value_column,
560                for_column,
561                in_columns,
562                alias,
563                alias_quote_style,
564            } => {
565                self.gen_table_source(source);
566                self.write(" ");
567                self.write_keyword("UNPIVOT");
568                self.write(" (");
569                self.write(value_column);
570                self.write(" ");
571                self.write_keyword("FOR ");
572                self.write(for_column);
573                self.write(" ");
574                self.write_keyword("IN ");
575                self.write("(");
576                self.gen_pivot_values(in_columns);
577                self.write("))");
578                if let Some(alias) = alias {
579                    self.write(" ");
580                    if !self.omit_table_alias_as() {
581                        self.write_keyword("AS ");
582                    }
583                    self.write_quoted(alias, *alias_quote_style);
584                }
585            }
586        }
587    }
588
589    fn gen_pivot_values(&mut self, values: &[PivotValue]) {
590        for (i, pv) in values.iter().enumerate() {
591            if i > 0 {
592                self.write(", ");
593            }
594            self.gen_expr(&pv.value);
595            if let Some(alias) = &pv.alias {
596                self.write(" ");
597                self.write_keyword("AS ");
598                self.write_quoted(alias, pv.alias_quote_style);
599            }
600        }
601    }
602
603    /// Returns true if the target dialect forbids `AS` in table aliases.
604    fn omit_table_alias_as(&self) -> bool {
605        matches!(self.dialect, Some(Dialect::Oracle))
606    }
607
608    fn gen_table_ref(&mut self, table: &TableRef) {
609        if let Some(catalog) = &table.catalog {
610            self.write(catalog);
611            self.write(".");
612        }
613        if let Some(schema) = &table.schema {
614            self.write(schema);
615            self.write(".");
616        }
617        self.write_quoted(&table.name, table.name_quote_style);
618        if let Some(alias) = &table.alias {
619            self.write(" ");
620            if !self.omit_table_alias_as() {
621                self.write_keyword("AS ");
622            }
623            self.write_quoted(alias, table.alias_quote_style);
624        }
625    }
626
627    fn gen_join(&mut self, join: &JoinClause) {
628        let join_kw = match join.join_type {
629            JoinType::Inner => "INNER JOIN",
630            JoinType::Left => "LEFT JOIN",
631            JoinType::Right => "RIGHT JOIN",
632            JoinType::Full => "FULL JOIN",
633            JoinType::Cross => "CROSS JOIN",
634            JoinType::Natural => "NATURAL JOIN",
635            JoinType::Lateral => "LATERAL JOIN",
636        };
637        self.sep();
638        self.write_keyword(join_kw);
639        if self.pretty {
640            self.indent_up();
641            self.newline();
642            self.gen_table_source(&join.table);
643        } else {
644            self.write(" ");
645            self.gen_table_source(&join.table);
646        }
647        if let Some(on) = &join.on {
648            if self.pretty {
649                self.newline();
650            } else {
651                self.write(" ");
652            }
653            self.write_keyword("ON ");
654            self.gen_expr(on);
655        }
656        if !join.using.is_empty() {
657            if self.pretty {
658                self.newline();
659            } else {
660                self.write(" ");
661            }
662            self.write_keyword("USING (");
663            self.write(&join.using.join(", "));
664            self.write(")");
665        }
666        if self.pretty {
667            self.indent_down();
668        }
669    }
670
671    fn gen_order_by(&mut self, items: &[OrderByItem]) {
672        if items.is_empty() {
673            return;
674        }
675        self.sep();
676        self.write_keyword("ORDER BY");
677        if self.pretty {
678            self.indent_up();
679            self.newline();
680        } else {
681            self.write(" ");
682        }
683        for (i, item) in items.iter().enumerate() {
684            if i > 0 {
685                self.write(", ");
686            }
687            self.gen_expr(&item.expr);
688            if !item.ascending {
689                self.write(" ");
690                self.write_keyword("DESC");
691            }
692            if let Some(nulls_first) = item.nulls_first {
693                if nulls_first {
694                    self.write(" ");
695                    self.write_keyword("NULLS FIRST");
696                } else {
697                    self.write(" ");
698                    self.write_keyword("NULLS LAST");
699                }
700            }
701        }
702        if self.pretty {
703            self.indent_down();
704        }
705    }
706
707    // ── Set operations ──────────────────────────────────────────
708
709    fn gen_set_operation(&mut self, sop: &SetOperationStatement) {
710        self.gen_statement(&sop.left);
711        let op_kw = match sop.op {
712            SetOperationType::Union => "UNION",
713            SetOperationType::Intersect => "INTERSECT",
714            SetOperationType::Except => "EXCEPT",
715        };
716        self.sep();
717        self.write_keyword(op_kw);
718        if sop.all {
719            self.write(" ");
720            self.write_keyword("ALL");
721        }
722        self.sep();
723        self.gen_statement(&sop.right);
724
725        self.gen_order_by(&sop.order_by);
726
727        if let Some(limit) = &sop.limit {
728            self.sep();
729            self.write_keyword("LIMIT ");
730            self.gen_expr(limit);
731        }
732        if let Some(offset) = &sop.offset {
733            self.sep();
734            self.write_keyword("OFFSET ");
735            self.gen_expr(offset);
736        }
737    }
738
739    // ── INSERT ──────────────────────────────────────────────────
740
741    fn gen_insert(&mut self, ins: &InsertStatement) {
742        self.write_keyword("INSERT INTO ");
743        self.gen_table_ref(&ins.table);
744
745        if !ins.columns.is_empty() {
746            self.write(" (");
747            self.write(&ins.columns.join(", "));
748            self.write(")");
749        }
750
751        // T-SQL: OUTPUT goes before VALUES
752        if matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric))
753            && !ins.returning.is_empty()
754        {
755            self.sep();
756            self.gen_output_clause(&ins.returning, "INSERTED");
757        }
758
759        match &ins.source {
760            InsertSource::Values(rows) => {
761                self.sep();
762                self.write_keyword("VALUES");
763                if self.pretty {
764                    self.indent_up();
765                    for (i, row) in rows.iter().enumerate() {
766                        self.newline();
767                        self.write("(");
768                        self.gen_expr_list(row);
769                        self.write(")");
770                        if i < rows.len() - 1 {
771                            self.write(",");
772                        }
773                    }
774                    self.indent_down();
775                } else {
776                    self.write(" ");
777                    for (i, row) in rows.iter().enumerate() {
778                        if i > 0 {
779                            self.write(", ");
780                        }
781                        self.write("(");
782                        self.gen_expr_list(row);
783                        self.write(")");
784                    }
785                }
786            }
787            InsertSource::Query(query) => {
788                self.sep();
789                self.gen_statement(query);
790            }
791            InsertSource::Default => {
792                self.sep();
793                self.write_keyword("DEFAULT VALUES");
794            }
795        }
796
797        if let Some(on_conflict) = &ins.on_conflict {
798            self.sep();
799            self.write_keyword("ON CONFLICT");
800            if !on_conflict.columns.is_empty() {
801                self.write(" (");
802                self.write(&on_conflict.columns.join(", "));
803                self.write(")");
804            }
805            match &on_conflict.action {
806                ConflictAction::DoNothing => {
807                    self.write(" ");
808                    self.write_keyword("DO NOTHING");
809                }
810                ConflictAction::DoUpdate(assignments) => {
811                    self.write(" ");
812                    self.write_keyword("DO UPDATE SET ");
813                    for (i, (col, val)) in assignments.iter().enumerate() {
814                        if i > 0 {
815                            self.write(", ");
816                        }
817                        self.write(col);
818                        self.write(" = ");
819                        self.gen_expr(val);
820                    }
821                }
822            }
823        }
824
825        // Non-T-SQL: RETURNING goes after VALUES
826        if !matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric))
827            && !ins.returning.is_empty()
828        {
829            self.sep();
830            self.write_keyword("RETURNING ");
831            for (i, item) in ins.returning.iter().enumerate() {
832                if i > 0 {
833                    self.write(", ");
834                }
835                self.gen_select_item(item);
836            }
837        }
838    }
839
840    // ── UPDATE ──────────────────────────────────────────────────
841
842    fn gen_update(&mut self, upd: &UpdateStatement) {
843        self.write_keyword("UPDATE ");
844        self.gen_table_ref(&upd.table);
845        self.sep();
846        self.write_keyword("SET");
847
848        if self.pretty {
849            self.indent_up();
850            for (i, (col, val)) in upd.assignments.iter().enumerate() {
851                self.newline();
852                self.write(col);
853                self.write(" = ");
854                self.gen_expr(val);
855                if i < upd.assignments.len() - 1 {
856                    self.write(",");
857                }
858            }
859            self.indent_down();
860        } else {
861            self.write(" ");
862            for (i, (col, val)) in upd.assignments.iter().enumerate() {
863                if i > 0 {
864                    self.write(", ");
865                }
866                self.write(col);
867                self.write(" = ");
868                self.gen_expr(val);
869            }
870        }
871
872        if let Some(from) = &upd.from {
873            self.sep();
874            self.write_keyword("FROM ");
875            self.gen_table_source(&from.source);
876        }
877
878        if let Some(wh) = &upd.where_clause {
879            self.sep();
880            self.write_keyword("WHERE");
881            if self.pretty {
882                self.indent_up();
883                self.newline();
884                self.gen_expr(wh);
885                self.indent_down();
886            } else {
887                self.write(" ");
888                self.gen_expr(wh);
889            }
890        }
891
892        if !upd.returning.is_empty() {
893            self.sep();
894            if matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric)) {
895                self.gen_output_clause(&upd.returning, "INSERTED");
896            } else {
897                self.write_keyword("RETURNING ");
898                for (i, item) in upd.returning.iter().enumerate() {
899                    if i > 0 {
900                        self.write(", ");
901                    }
902                    self.gen_select_item(item);
903                }
904            }
905        }
906    }
907
908    // ── DELETE ──────────────────────────────────────────────────
909
910    fn gen_delete(&mut self, del: &DeleteStatement) {
911        self.write_keyword("DELETE FROM ");
912        self.gen_table_ref(&del.table);
913
914        if let Some(using) = &del.using {
915            self.sep();
916            self.write_keyword("USING ");
917            self.gen_table_source(&using.source);
918        }
919
920        // T-SQL: OUTPUT clause comes before WHERE
921        if !del.returning.is_empty()
922            && matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric))
923        {
924            self.sep();
925            self.gen_output_clause(&del.returning, "DELETED");
926        }
927
928        if let Some(wh) = &del.where_clause {
929            self.sep();
930            self.write_keyword("WHERE");
931            if self.pretty {
932                self.indent_up();
933                self.newline();
934                self.gen_expr(wh);
935                self.indent_down();
936            } else {
937                self.write(" ");
938                self.gen_expr(wh);
939            }
940        }
941
942        // Non-T-SQL: RETURNING after WHERE
943        if !del.returning.is_empty()
944            && !matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric))
945        {
946            self.sep();
947            self.write_keyword("RETURNING ");
948            for (i, item) in del.returning.iter().enumerate() {
949                if i > 0 {
950                    self.write(", ");
951                }
952                self.gen_select_item(item);
953            }
954        }
955    }
956
957    // ── CREATE TABLE ────────────────────────────────────────────
958
959    /// Emit a T-SQL OUTPUT clause: `OUTPUT prefix.col1, prefix.col2`
960    fn gen_output_clause(&mut self, items: &[SelectItem], prefix: &str) {
961        self.write_keyword("OUTPUT ");
962        for (i, item) in items.iter().enumerate() {
963            if i > 0 {
964                self.write(", ");
965            }
966            match item {
967                SelectItem::Wildcard => {
968                    self.write(prefix);
969                    self.write(".*");
970                }
971                SelectItem::QualifiedWildcard { table } => {
972                    self.write(prefix);
973                    self.write(".");
974                    self.write(table);
975                    self.write(".*");
976                }
977                SelectItem::Expr { expr, .. } => match expr {
978                    Expr::Column { name, .. } => {
979                        self.write(prefix);
980                        self.write(".");
981                        self.write(name);
982                    }
983                    Expr::Star | Expr::Wildcard => {
984                        self.write(prefix);
985                        self.write(".*");
986                    }
987                    _ => {
988                        self.write(prefix);
989                        self.write(".");
990                        self.gen_expr(expr);
991                    }
992                },
993            }
994        }
995    }
996
997    // ── MERGE ───────────────────────────────────────────────────
998
999    fn gen_merge(&mut self, merge: &MergeStatement) {
1000        self.write_keyword("MERGE INTO ");
1001        self.gen_table_ref(&merge.target);
1002
1003        self.sep();
1004        self.write_keyword("USING ");
1005        self.gen_table_source(&merge.source);
1006
1007        self.sep();
1008        self.write_keyword("ON");
1009        if self.pretty {
1010            self.indent_up();
1011            self.newline();
1012            self.gen_expr(&merge.on);
1013            self.indent_down();
1014        } else {
1015            self.write(" ");
1016            self.gen_expr(&merge.on);
1017        }
1018
1019        for clause in &merge.clauses {
1020            self.sep();
1021            self.gen_merge_clause(clause);
1022        }
1023
1024        if !merge.output.is_empty() {
1025            self.sep();
1026            self.write_keyword("OUTPUT ");
1027            for (i, item) in merge.output.iter().enumerate() {
1028                if i > 0 {
1029                    self.write(", ");
1030                }
1031                self.gen_select_item(item);
1032            }
1033        }
1034    }
1035
1036    fn gen_merge_clause(&mut self, clause: &MergeClause) {
1037        self.write_keyword("WHEN ");
1038        match &clause.kind {
1039            MergeClauseKind::Matched => self.write_keyword("MATCHED"),
1040            MergeClauseKind::NotMatched => self.write_keyword("NOT MATCHED"),
1041            MergeClauseKind::NotMatchedBySource => self.write_keyword("NOT MATCHED BY SOURCE"),
1042        }
1043
1044        if let Some(cond) = &clause.condition {
1045            self.write_keyword(" AND ");
1046            self.gen_expr(cond);
1047        }
1048
1049        self.write_keyword(" THEN");
1050
1051        match &clause.action {
1052            MergeAction::Update(assignments) => {
1053                self.sep();
1054                self.write_keyword("UPDATE SET");
1055                if self.pretty {
1056                    self.indent_up();
1057                    for (i, (col, val)) in assignments.iter().enumerate() {
1058                        self.newline();
1059                        self.write(col);
1060                        self.write(" = ");
1061                        self.gen_expr(val);
1062                        if i < assignments.len() - 1 {
1063                            self.write(",");
1064                        }
1065                    }
1066                    self.indent_down();
1067                } else {
1068                    self.write(" ");
1069                    for (i, (col, val)) in assignments.iter().enumerate() {
1070                        if i > 0 {
1071                            self.write(", ");
1072                        }
1073                        self.write(col);
1074                        self.write(" = ");
1075                        self.gen_expr(val);
1076                    }
1077                }
1078            }
1079            MergeAction::Insert { columns, values } => {
1080                self.sep();
1081                self.write_keyword("INSERT");
1082                if !columns.is_empty() {
1083                    self.write(" (");
1084                    self.write(&columns.join(", "));
1085                    self.write(")");
1086                }
1087                self.write_keyword(" VALUES");
1088                self.write(" (");
1089                for (i, val) in values.iter().enumerate() {
1090                    if i > 0 {
1091                        self.write(", ");
1092                    }
1093                    self.gen_expr(val);
1094                }
1095                self.write(")");
1096            }
1097            MergeAction::InsertRow => {
1098                self.sep();
1099                self.write_keyword("INSERT ROW");
1100            }
1101            MergeAction::Delete => {
1102                self.sep();
1103                self.write_keyword("DELETE");
1104            }
1105        }
1106    }
1107
1108    // ── CREATE TABLE ────────────────────────────────────────────
1109
1110    fn gen_create_table(&mut self, ct: &CreateTableStatement) {
1111        self.write_keyword("CREATE ");
1112        if ct.temporary {
1113            self.write_keyword("TEMPORARY ");
1114        }
1115        self.write_keyword("TABLE ");
1116        if ct.if_not_exists {
1117            self.write_keyword("IF NOT EXISTS ");
1118        }
1119        self.gen_table_ref(&ct.table);
1120
1121        if let Some(as_select) = &ct.as_select {
1122            self.write(" ");
1123            self.write_keyword("AS ");
1124            self.gen_statement(as_select);
1125            return;
1126        }
1127
1128        self.write(" (");
1129
1130        if self.pretty {
1131            self.indent_up();
1132            for (i, col) in ct.columns.iter().enumerate() {
1133                self.newline();
1134                self.gen_column_def(col);
1135                if i < ct.columns.len() - 1 || !ct.constraints.is_empty() {
1136                    self.write(",");
1137                }
1138            }
1139            for (i, constraint) in ct.constraints.iter().enumerate() {
1140                self.newline();
1141                self.gen_table_constraint(constraint);
1142                if i < ct.constraints.len() - 1 {
1143                    self.write(",");
1144                }
1145            }
1146            self.indent_down();
1147            self.newline();
1148        } else {
1149            for (i, col) in ct.columns.iter().enumerate() {
1150                if i > 0 {
1151                    self.write(", ");
1152                }
1153                self.gen_column_def(col);
1154            }
1155            for (i, constraint) in ct.constraints.iter().enumerate() {
1156                if i + ct.columns.len() > 0 {
1157                    self.write(", ");
1158                }
1159                self.gen_table_constraint(constraint);
1160            }
1161        }
1162
1163        self.write(")");
1164    }
1165
1166    fn gen_column_def(&mut self, col: &ColumnDef) {
1167        self.write(&col.name);
1168        self.write(" ");
1169        self.gen_data_type(&col.data_type);
1170
1171        if col.primary_key {
1172            self.write(" ");
1173            self.write_keyword("PRIMARY KEY");
1174        }
1175        if col.unique {
1176            self.write(" ");
1177            self.write_keyword("UNIQUE");
1178        }
1179        if col.auto_increment {
1180            self.write(" ");
1181            self.write_keyword("AUTOINCREMENT");
1182        }
1183
1184        match col.nullable {
1185            Some(false) => {
1186                self.write(" ");
1187                self.write_keyword("NOT NULL");
1188            }
1189            Some(true) => {
1190                self.write(" ");
1191                self.write_keyword("NULL");
1192            }
1193            None => {}
1194        }
1195
1196        if let Some(default) = &col.default {
1197            self.write(" ");
1198            self.write_keyword("DEFAULT ");
1199            self.gen_expr(default);
1200        }
1201
1202        if let Some(collation) = &col.collation {
1203            self.write(" ");
1204            self.write_keyword("COLLATE ");
1205            self.write(collation);
1206        }
1207
1208        if let Some(comment) = &col.comment {
1209            self.write(" ");
1210            self.write_keyword("COMMENT '");
1211            self.write(&comment.replace('\'', "''"));
1212            self.write("'");
1213        }
1214    }
1215
1216    fn gen_table_constraint(&mut self, constraint: &TableConstraint) {
1217        match constraint {
1218            TableConstraint::PrimaryKey { name, columns } => {
1219                if let Some(name) = name {
1220                    self.write_keyword("CONSTRAINT ");
1221                    self.write(name);
1222                    self.write(" ");
1223                }
1224                self.write_keyword("PRIMARY KEY (");
1225                self.write(&columns.join(", "));
1226                self.write(")");
1227            }
1228            TableConstraint::Unique { name, columns } => {
1229                if let Some(name) = name {
1230                    self.write_keyword("CONSTRAINT ");
1231                    self.write(name);
1232                    self.write(" ");
1233                }
1234                self.write_keyword("UNIQUE (");
1235                self.write(&columns.join(", "));
1236                self.write(")");
1237            }
1238            TableConstraint::ForeignKey {
1239                name,
1240                columns,
1241                ref_table,
1242                ref_columns,
1243                on_delete,
1244                on_update,
1245            } => {
1246                if let Some(name) = name {
1247                    self.write_keyword("CONSTRAINT ");
1248                    self.write(name);
1249                    self.write(" ");
1250                }
1251                self.write_keyword("FOREIGN KEY (");
1252                self.write(&columns.join(", "));
1253                self.write(") ");
1254                self.write_keyword("REFERENCES ");
1255                self.gen_table_ref(ref_table);
1256                self.write(" (");
1257                self.write(&ref_columns.join(", "));
1258                self.write(")");
1259                if let Some(action) = on_delete {
1260                    self.write(" ");
1261                    self.write_keyword("ON DELETE ");
1262                    self.gen_referential_action(action);
1263                }
1264                if let Some(action) = on_update {
1265                    self.write(" ");
1266                    self.write_keyword("ON UPDATE ");
1267                    self.gen_referential_action(action);
1268                }
1269            }
1270            TableConstraint::Check { name, expr } => {
1271                if let Some(name) = name {
1272                    self.write_keyword("CONSTRAINT ");
1273                    self.write(name);
1274                    self.write(" ");
1275                }
1276                self.write_keyword("CHECK (");
1277                self.gen_expr(expr);
1278                self.write(")");
1279            }
1280        }
1281    }
1282
1283    fn gen_referential_action(&mut self, action: &ReferentialAction) {
1284        match action {
1285            ReferentialAction::Cascade => self.write_keyword("CASCADE"),
1286            ReferentialAction::Restrict => self.write_keyword("RESTRICT"),
1287            ReferentialAction::NoAction => self.write_keyword("NO ACTION"),
1288            ReferentialAction::SetNull => self.write_keyword("SET NULL"),
1289            ReferentialAction::SetDefault => self.write_keyword("SET DEFAULT"),
1290        }
1291    }
1292
1293    // ── DROP TABLE ──────────────────────────────────────────────
1294
1295    fn gen_drop_table(&mut self, dt: &DropTableStatement) {
1296        self.write_keyword("DROP TABLE ");
1297        if dt.if_exists {
1298            self.write_keyword("IF EXISTS ");
1299        }
1300        self.gen_table_ref(&dt.table);
1301        if dt.cascade {
1302            self.write(" ");
1303            self.write_keyword("CASCADE");
1304        }
1305    }
1306
1307    // ── ALTER TABLE ─────────────────────────────────────────────
1308
1309    fn gen_alter_table(&mut self, alt: &AlterTableStatement) {
1310        self.write_keyword("ALTER TABLE ");
1311        self.gen_table_ref(&alt.table);
1312
1313        for (i, action) in alt.actions.iter().enumerate() {
1314            if i > 0 {
1315                self.write(",");
1316            }
1317            self.write(" ");
1318            match action {
1319                AlterTableAction::AddColumn(col) => {
1320                    self.write_keyword("ADD COLUMN ");
1321                    self.gen_column_def(col);
1322                }
1323                AlterTableAction::DropColumn { name, if_exists } => {
1324                    self.write_keyword("DROP COLUMN ");
1325                    if *if_exists {
1326                        self.write_keyword("IF EXISTS ");
1327                    }
1328                    self.write(name);
1329                }
1330                AlterTableAction::RenameColumn { old_name, new_name } => {
1331                    self.write_keyword("RENAME COLUMN ");
1332                    self.write(old_name);
1333                    self.write(" ");
1334                    self.write_keyword("TO ");
1335                    self.write(new_name);
1336                }
1337                AlterTableAction::AlterColumnType { name, data_type } => {
1338                    self.write_keyword("ALTER COLUMN ");
1339                    self.write(name);
1340                    self.write(" ");
1341                    self.write_keyword("TYPE ");
1342                    self.gen_data_type(data_type);
1343                }
1344                AlterTableAction::AddConstraint(constraint) => {
1345                    self.write_keyword("ADD ");
1346                    self.gen_table_constraint(constraint);
1347                }
1348                AlterTableAction::DropConstraint { name } => {
1349                    self.write_keyword("DROP CONSTRAINT ");
1350                    self.write(name);
1351                }
1352                AlterTableAction::RenameTable { new_name } => {
1353                    self.write_keyword("RENAME TO ");
1354                    self.write(new_name);
1355                }
1356            }
1357        }
1358    }
1359
1360    // ── CREATE / DROP VIEW ──────────────────────────────────────
1361
1362    fn gen_create_view(&mut self, cv: &CreateViewStatement) {
1363        self.write_keyword("CREATE ");
1364        if cv.or_replace {
1365            self.write_keyword("OR REPLACE ");
1366        }
1367        if cv.materialized {
1368            self.write_keyword("MATERIALIZED ");
1369        }
1370        self.write_keyword("VIEW ");
1371        if cv.if_not_exists {
1372            self.write_keyword("IF NOT EXISTS ");
1373        }
1374        self.gen_table_ref(&cv.name);
1375
1376        if !cv.columns.is_empty() {
1377            self.write(" (");
1378            self.write(&cv.columns.join(", "));
1379            self.write(")");
1380        }
1381
1382        self.write(" ");
1383        self.write_keyword("AS ");
1384        self.gen_statement(&cv.query);
1385    }
1386
1387    fn gen_drop_view(&mut self, dv: &DropViewStatement) {
1388        self.write_keyword("DROP ");
1389        if dv.materialized {
1390            self.write_keyword("MATERIALIZED ");
1391        }
1392        self.write_keyword("VIEW ");
1393        if dv.if_exists {
1394            self.write_keyword("IF EXISTS ");
1395        }
1396        self.gen_table_ref(&dv.name);
1397    }
1398
1399    // ── TRUNCATE ────────────────────────────────────────────────
1400
1401    fn gen_truncate(&mut self, t: &TruncateStatement) {
1402        self.write_keyword("TRUNCATE TABLE ");
1403        self.gen_table_ref(&t.table);
1404    }
1405
1406    // ── Transaction ─────────────────────────────────────────────
1407
1408    fn gen_transaction(&mut self, t: &TransactionStatement) {
1409        match t {
1410            TransactionStatement::Begin => self.write_keyword("BEGIN"),
1411            TransactionStatement::Commit => self.write_keyword("COMMIT"),
1412            TransactionStatement::Rollback => self.write_keyword("ROLLBACK"),
1413            TransactionStatement::Savepoint(name) => {
1414                self.write_keyword("SAVEPOINT ");
1415                self.write(name);
1416            }
1417            TransactionStatement::ReleaseSavepoint(name) => {
1418                self.write_keyword("RELEASE SAVEPOINT ");
1419                self.write(name);
1420            }
1421            TransactionStatement::RollbackTo(name) => {
1422                self.write_keyword("ROLLBACK TO SAVEPOINT ");
1423                self.write(name);
1424            }
1425        }
1426    }
1427
1428    // ── EXPLAIN ─────────────────────────────────────────────────
1429
1430    fn gen_explain(&mut self, e: &ExplainStatement) {
1431        self.write_keyword("EXPLAIN ");
1432        if e.analyze {
1433            self.write_keyword("ANALYZE ");
1434        }
1435        self.gen_statement(&e.statement);
1436    }
1437
1438    // ── USE ─────────────────────────────────────────────────────
1439
1440    fn gen_use(&mut self, u: &UseStatement) {
1441        self.write_keyword("USE ");
1442        self.write(&u.name);
1443    }
1444
1445    // ══════════════════════════════════════════════════════════════
1446    // Data types
1447    // ══════════════════════════════════════════════════════════════
1448
1449    fn gen_data_type(&mut self, dt: &DataType) {
1450        match dt {
1451            DataType::TinyInt => self.write("TINYINT"),
1452            DataType::SmallInt => self.write("SMALLINT"),
1453            DataType::Int => self.write("INT"),
1454            DataType::BigInt => self.write("BIGINT"),
1455            DataType::Float => self.write("FLOAT"),
1456            DataType::Double => self.write("DOUBLE"),
1457            DataType::Real => self.write("REAL"),
1458            DataType::Decimal { precision, scale } | DataType::Numeric { precision, scale } => {
1459                self.write(if matches!(dt, DataType::Numeric { .. }) {
1460                    "NUMERIC"
1461                } else {
1462                    "DECIMAL"
1463                });
1464                if let Some(p) = precision {
1465                    self.write(&format!("({p}"));
1466                    if let Some(s) = scale {
1467                        self.write(&format!(", {s}"));
1468                    }
1469                    self.write(")");
1470                }
1471            }
1472            DataType::Varchar(len) => {
1473                self.write("VARCHAR");
1474                if let Some(n) = len {
1475                    self.write(&format!("({n})"));
1476                }
1477            }
1478            DataType::Char(len) => {
1479                self.write("CHAR");
1480                if let Some(n) = len {
1481                    self.write(&format!("({n})"));
1482                }
1483            }
1484            DataType::Text => self.write("TEXT"),
1485            DataType::String => self.write("STRING"),
1486            DataType::Binary(len) => {
1487                self.write("BINARY");
1488                if let Some(n) = len {
1489                    self.write(&format!("({n})"));
1490                }
1491            }
1492            DataType::Varbinary(len) => {
1493                self.write("VARBINARY");
1494                if let Some(n) = len {
1495                    self.write(&format!("({n})"));
1496                }
1497            }
1498            DataType::Boolean => self.write("BOOLEAN"),
1499            DataType::Date => self.write("DATE"),
1500            DataType::Time { precision } => {
1501                self.write("TIME");
1502                if let Some(p) = precision {
1503                    self.write(&format!("({p})"));
1504                }
1505            }
1506            DataType::Timestamp { precision, with_tz } => {
1507                self.write("TIMESTAMP");
1508                if let Some(p) = precision {
1509                    self.write(&format!("({p})"));
1510                }
1511                if *with_tz {
1512                    self.write(" WITH TIME ZONE");
1513                }
1514            }
1515            DataType::Interval => self.write("INTERVAL"),
1516            DataType::DateTime => self.write("DATETIME"),
1517            DataType::Blob => self.write("BLOB"),
1518            DataType::Bytea => self.write("BYTEA"),
1519            DataType::Bytes => self.write("BYTES"),
1520            DataType::Json => self.write("JSON"),
1521            DataType::Jsonb => self.write("JSONB"),
1522            DataType::Uuid => self.write("UUID"),
1523            DataType::Array(inner) => {
1524                let is_postgres = matches!(
1525                    self.dialect,
1526                    Some(
1527                        Dialect::Postgres
1528                            | Dialect::Redshift
1529                            | Dialect::Materialize
1530                            | Dialect::RisingWave
1531                    )
1532                );
1533                if is_postgres {
1534                    // PostgreSQL: emit "typename[]"
1535                    if let Some(inner) = inner {
1536                        self.gen_data_type(inner);
1537                        self.write("[]");
1538                    } else {
1539                        self.write("ARRAY");
1540                    }
1541                } else {
1542                    self.write("ARRAY");
1543                    if let Some(inner) = inner {
1544                        self.write("<");
1545                        self.gen_data_type(inner);
1546                        self.write(">");
1547                    }
1548                }
1549            }
1550            DataType::Map { key, value } => {
1551                self.write("MAP<");
1552                self.gen_data_type(key);
1553                self.write(", ");
1554                self.gen_data_type(value);
1555                self.write(">");
1556            }
1557            DataType::Struct(fields) => {
1558                self.write("STRUCT<");
1559                for (i, (name, dt)) in fields.iter().enumerate() {
1560                    if i > 0 {
1561                        self.write(", ");
1562                    }
1563                    self.write(name);
1564                    self.write(" ");
1565                    self.gen_data_type(dt);
1566                }
1567                self.write(">");
1568            }
1569            DataType::Tuple(types) => {
1570                self.write("TUPLE(");
1571                for (i, dt) in types.iter().enumerate() {
1572                    if i > 0 {
1573                        self.write(", ");
1574                    }
1575                    self.gen_data_type(dt);
1576                }
1577                self.write(")");
1578            }
1579            DataType::Null => self.write("NULL"),
1580            DataType::Variant => self.write("VARIANT"),
1581            DataType::Object => self.write("OBJECT"),
1582            DataType::Xml => self.write("XML"),
1583            DataType::Inet => self.write("INET"),
1584            DataType::Cidr => self.write("CIDR"),
1585            DataType::Macaddr => self.write("MACADDR"),
1586            DataType::Bit(len) => {
1587                self.write("BIT");
1588                if let Some(n) = len {
1589                    self.write(&format!("({n})"));
1590                }
1591            }
1592            DataType::Money => self.write("MONEY"),
1593            DataType::Serial => self.write("SERIAL"),
1594            DataType::BigSerial => self.write("BIGSERIAL"),
1595            DataType::SmallSerial => self.write("SMALLSERIAL"),
1596            DataType::Regclass => self.write("REGCLASS"),
1597            DataType::Regtype => self.write("REGTYPE"),
1598            DataType::Hstore => self.write("HSTORE"),
1599            DataType::Geography => self.write("GEOGRAPHY"),
1600            DataType::Geometry => self.write("GEOMETRY"),
1601            DataType::Super => self.write("SUPER"),
1602            DataType::Unknown(name) => self.write(name),
1603        }
1604    }
1605
1606    // ══════════════════════════════════════════════════════════════
1607    // Expressions
1608    // ══════════════════════════════════════════════════════════════
1609
1610    fn binary_op_str(op: &BinaryOperator) -> &'static str {
1611        match op {
1612            BinaryOperator::Plus => " + ",
1613            BinaryOperator::Minus => " - ",
1614            BinaryOperator::Multiply => " * ",
1615            BinaryOperator::Divide => " / ",
1616            BinaryOperator::Modulo => " % ",
1617            BinaryOperator::Eq => " = ",
1618            BinaryOperator::Neq => " <> ",
1619            BinaryOperator::Lt => " < ",
1620            BinaryOperator::Gt => " > ",
1621            BinaryOperator::LtEq => " <= ",
1622            BinaryOperator::GtEq => " >= ",
1623            BinaryOperator::And => " AND ",
1624            BinaryOperator::Or => " OR ",
1625            BinaryOperator::Xor => " XOR ",
1626            BinaryOperator::Concat => " || ",
1627            BinaryOperator::BitwiseAnd => " & ",
1628            BinaryOperator::BitwiseOr => " | ",
1629            BinaryOperator::BitwiseXor => " ^ ",
1630            BinaryOperator::ShiftLeft => " << ",
1631            BinaryOperator::ShiftRight => " >> ",
1632            BinaryOperator::Arrow => " -> ",
1633            BinaryOperator::DoubleArrow => " ->> ",
1634        }
1635    }
1636
1637    fn gen_expr_list(&mut self, exprs: &[Expr]) {
1638        for (i, expr) in exprs.iter().enumerate() {
1639            if i > 0 {
1640                self.write(", ");
1641            }
1642            self.gen_expr(expr);
1643        }
1644    }
1645
1646    fn gen_expr(&mut self, expr: &Expr) {
1647        match expr {
1648            Expr::Column {
1649                table,
1650                name,
1651                quote_style,
1652                table_quote_style,
1653            } => {
1654                if let Some(t) = table {
1655                    self.write_quoted(t, *table_quote_style);
1656                    self.write(".");
1657                }
1658                self.write_quoted(name, *quote_style);
1659            }
1660            Expr::Number(n) => self.write(n),
1661            Expr::StringLiteral(s) => {
1662                // TSQL and Oracle require N'...' prefix for string literals containing
1663                // non-ASCII characters to prevent code-page corruption (CR-007).
1664                if matches!(self.dialect, Some(Dialect::Oracle) | Some(Dialect::Tsql))
1665                    && !s.is_ascii()
1666                {
1667                    self.write("N'");
1668                } else {
1669                    self.write("'");
1670                }
1671                self.write(&s.replace('\'', "''"));
1672                self.write("'");
1673            }
1674            Expr::NationalStringLiteral(s) => {
1675                if matches!(self.dialect, Some(Dialect::Oracle) | Some(Dialect::Tsql)) {
1676                    self.write("N'");
1677                } else {
1678                    self.write("'");
1679                }
1680                self.write(&s.replace('\'', "''"));
1681                self.write("'");
1682            }
1683            Expr::Boolean(b) => {
1684                if matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric)) {
1685                    self.write(if *b { "1" } else { "0" });
1686                } else {
1687                    self.write(if *b { "TRUE" } else { "FALSE" });
1688                }
1689            }
1690            Expr::Null => self.write("NULL"),
1691            Expr::Default => self.write_keyword("DEFAULT"),
1692            Expr::Wildcard | Expr::Star => self.write("*"),
1693
1694            Expr::Cube { exprs } => {
1695                self.write_keyword("CUBE");
1696                self.write("(");
1697                self.gen_expr_list(exprs);
1698                self.write(")");
1699            }
1700            Expr::Rollup { exprs } => {
1701                self.write_keyword("ROLLUP");
1702                self.write("(");
1703                self.gen_expr_list(exprs);
1704                self.write(")");
1705            }
1706            Expr::GroupingSets { sets } => {
1707                self.write_keyword("GROUPING SETS");
1708                self.write("(");
1709                self.gen_expr_list(sets);
1710                self.write(")");
1711            }
1712
1713            Expr::BinaryOp { left, op, right } => {
1714                self.gen_expr(left);
1715                self.write(Self::binary_op_str(op));
1716                self.gen_expr(right);
1717            }
1718            Expr::AnyOp { expr, op, right } => {
1719                self.gen_expr(expr);
1720                self.write(Self::binary_op_str(op));
1721                self.write_keyword("ANY");
1722                self.write("(");
1723                if let Expr::Subquery(query) = right.as_ref() {
1724                    self.gen_statement(query);
1725                } else {
1726                    self.gen_expr(right);
1727                }
1728                self.write(")");
1729            }
1730            Expr::AllOp { expr, op, right } => {
1731                self.gen_expr(expr);
1732                self.write(Self::binary_op_str(op));
1733                self.write_keyword("ALL");
1734                self.write("(");
1735                if let Expr::Subquery(query) = right.as_ref() {
1736                    self.gen_statement(query);
1737                } else {
1738                    self.gen_expr(right);
1739                }
1740                self.write(")");
1741            }
1742            Expr::UnaryOp { op, expr } => {
1743                let op_str = match op {
1744                    UnaryOperator::Not => "NOT ",
1745                    UnaryOperator::Minus => "-",
1746                    UnaryOperator::Plus => "+",
1747                    UnaryOperator::BitwiseNot => "~",
1748                };
1749                self.write(op_str);
1750                self.gen_expr(expr);
1751            }
1752            Expr::Function {
1753                name,
1754                args,
1755                distinct,
1756                filter,
1757                over,
1758            } => {
1759                self.write(name);
1760                self.write("(");
1761                if *distinct {
1762                    self.write_keyword("DISTINCT ");
1763                }
1764                self.gen_expr_list(args);
1765                self.write(")");
1766
1767                if let Some(filter_expr) = filter {
1768                    self.write(" ");
1769                    self.write_keyword("FILTER (WHERE ");
1770                    self.gen_expr(filter_expr);
1771                    self.write(")");
1772                }
1773                if let Some(spec) = over {
1774                    self.write(" ");
1775                    self.write_keyword("OVER ");
1776                    if let Some(wref) = &spec.window_ref {
1777                        if spec.partition_by.is_empty()
1778                            && spec.order_by.is_empty()
1779                            && spec.frame.is_none()
1780                        {
1781                            self.write(wref);
1782                        } else {
1783                            self.write("(");
1784                            self.gen_window_spec(spec);
1785                            self.write(")");
1786                        }
1787                    } else {
1788                        self.write("(");
1789                        self.gen_window_spec(spec);
1790                        self.write(")");
1791                    }
1792                }
1793            }
1794            Expr::Between {
1795                expr,
1796                low,
1797                high,
1798                negated,
1799            } => {
1800                self.gen_expr(expr);
1801                if *negated {
1802                    self.write(" ");
1803                    self.write_keyword("NOT");
1804                }
1805                self.write(" ");
1806                self.write_keyword("BETWEEN ");
1807                self.gen_expr(low);
1808                self.write(" ");
1809                self.write_keyword("AND ");
1810                self.gen_expr(high);
1811            }
1812            Expr::InList {
1813                expr,
1814                list,
1815                negated,
1816            } => {
1817                self.gen_expr(expr);
1818                if *negated {
1819                    self.write(" ");
1820                    self.write_keyword("NOT");
1821                }
1822                self.write(" ");
1823                self.write_keyword("IN (");
1824                self.gen_expr_list(list);
1825                self.write(")");
1826            }
1827            Expr::InSubquery {
1828                expr,
1829                subquery,
1830                negated,
1831            } => {
1832                self.gen_expr(expr);
1833                if *negated {
1834                    self.write(" ");
1835                    self.write_keyword("NOT");
1836                }
1837                self.write(" ");
1838                self.write_keyword("IN (");
1839                self.gen_statement(subquery);
1840                self.write(")");
1841            }
1842            Expr::IsNull { expr, negated } => {
1843                self.gen_expr(expr);
1844                if *negated {
1845                    self.write(" ");
1846                    self.write_keyword("IS NOT NULL");
1847                } else {
1848                    self.write(" ");
1849                    self.write_keyword("IS NULL");
1850                }
1851            }
1852            Expr::IsBool {
1853                expr,
1854                value,
1855                negated,
1856            } => {
1857                self.gen_expr(expr);
1858                self.write(" ");
1859                match (negated, value) {
1860                    (false, true) => self.write_keyword("IS TRUE"),
1861                    (false, false) => self.write_keyword("IS FALSE"),
1862                    (true, true) => self.write_keyword("IS NOT TRUE"),
1863                    (true, false) => self.write_keyword("IS NOT FALSE"),
1864                }
1865            }
1866            Expr::Like {
1867                expr,
1868                pattern,
1869                negated,
1870                escape,
1871            } => {
1872                self.gen_expr(expr);
1873                if *negated {
1874                    self.write(" ");
1875                    self.write_keyword("NOT");
1876                }
1877                self.write(" ");
1878                self.write_keyword("LIKE ");
1879                self.gen_expr(pattern);
1880                if let Some(esc) = escape {
1881                    self.write(" ");
1882                    self.write_keyword("ESCAPE ");
1883                    self.gen_expr(esc);
1884                }
1885            }
1886            Expr::ILike {
1887                expr,
1888                pattern,
1889                negated,
1890                escape,
1891            } => {
1892                self.gen_expr(expr);
1893                if *negated {
1894                    self.write(" ");
1895                    self.write_keyword("NOT");
1896                }
1897                self.write(" ");
1898                self.write_keyword("ILIKE ");
1899                self.gen_expr(pattern);
1900                if let Some(esc) = escape {
1901                    self.write(" ");
1902                    self.write_keyword("ESCAPE ");
1903                    self.gen_expr(esc);
1904                }
1905            }
1906            Expr::SimilarTo {
1907                expr,
1908                pattern,
1909                negated,
1910                escape,
1911            } => {
1912                self.gen_expr(expr);
1913                if *negated {
1914                    self.write(" ");
1915                    self.write_keyword("NOT");
1916                }
1917                self.write(" ");
1918                self.write_keyword("SIMILAR TO ");
1919                self.gen_expr(pattern);
1920                if let Some(esc) = escape {
1921                    self.write(" ");
1922                    self.write_keyword("ESCAPE ");
1923                    self.gen_expr(esc);
1924                }
1925            }
1926            Expr::Case {
1927                operand,
1928                when_clauses,
1929                else_clause,
1930            } => {
1931                self.write_keyword("CASE");
1932                if let Some(op) = operand {
1933                    self.write(" ");
1934                    self.gen_expr(op);
1935                }
1936                for (cond, result) in when_clauses {
1937                    self.write(" ");
1938                    self.write_keyword("WHEN ");
1939                    self.gen_expr(cond);
1940                    self.write(" ");
1941                    self.write_keyword("THEN ");
1942                    self.gen_expr(result);
1943                }
1944                if let Some(el) = else_clause {
1945                    self.write(" ");
1946                    self.write_keyword("ELSE ");
1947                    self.gen_expr(el);
1948                }
1949                self.write(" ");
1950                self.write_keyword("END");
1951            }
1952            Expr::Nested(inner) => {
1953                self.write("(");
1954                self.gen_expr(inner);
1955                self.write(")");
1956            }
1957            Expr::Subquery(query) => {
1958                self.write("(");
1959                self.gen_statement(query);
1960                self.write(")");
1961            }
1962            Expr::Exists { subquery, negated } => {
1963                if *negated {
1964                    self.write_keyword("NOT ");
1965                }
1966                self.write_keyword("EXISTS (");
1967                self.gen_statement(subquery);
1968                self.write(")");
1969            }
1970            Expr::Cast { expr, data_type } => {
1971                // ANSI typed string literals: emit DATE 'x' / TIMESTAMP 'x' / TIME 'x'
1972                if let Expr::StringLiteral(val) = expr.as_ref() {
1973                    let ansi_keyword = match data_type {
1974                        DataType::Date => Some("DATE"),
1975                        DataType::Timestamp { .. } => Some("TIMESTAMP"),
1976                        DataType::Time { .. } => Some("TIME"),
1977                        _ => None,
1978                    };
1979                    if let Some(kw) = ansi_keyword {
1980                        let is_mysql_family = matches!(
1981                            self.dialect,
1982                            Some(
1983                                Dialect::Mysql
1984                                    | Dialect::Doris
1985                                    | Dialect::SingleStore
1986                                    | Dialect::StarRocks
1987                            )
1988                        );
1989                        if !is_mysql_family {
1990                            self.write_keyword(kw);
1991                            self.write(" '");
1992                            self.write(val);
1993                            self.write("'");
1994                            return;
1995                        }
1996                    }
1997                }
1998
1999                let is_postgres = matches!(
2000                    self.dialect,
2001                    Some(
2002                        Dialect::Postgres
2003                            | Dialect::Redshift
2004                            | Dialect::Materialize
2005                            | Dialect::RisingWave
2006                    )
2007                );
2008                if is_postgres {
2009                    self.gen_expr(expr);
2010                    self.write("::");
2011                    self.gen_data_type(data_type);
2012                } else {
2013                    self.write_keyword("CAST(");
2014                    self.gen_expr(expr);
2015                    self.write(" ");
2016                    self.write_keyword("AS ");
2017                    self.gen_data_type(data_type);
2018                    self.write(")");
2019                }
2020            }
2021            Expr::TryCast { expr, data_type } => {
2022                self.write_keyword("TRY_CAST(");
2023                self.gen_expr(expr);
2024                self.write(" ");
2025                self.write_keyword("AS ");
2026                self.gen_data_type(data_type);
2027                self.write(")");
2028            }
2029            Expr::Extract { field, expr } => {
2030                if matches!(self.dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric)) {
2031                    if *field == DateTimeField::Epoch {
2032                        // EXTRACT(EPOCH FROM x) → DATEDIFF(SECOND, '1970-01-01', x)
2033                        self.write_keyword("DATEDIFF(SECOND, '1970-01-01', ");
2034                        self.gen_expr(expr);
2035                        self.write(")");
2036                    } else {
2037                        self.write_keyword("DATEPART(");
2038                        self.gen_datetime_field(field);
2039                        self.write(", ");
2040                        self.gen_expr(expr);
2041                        self.write(")");
2042                    }
2043                } else {
2044                    self.write_keyword("EXTRACT(");
2045                    self.gen_datetime_field(field);
2046                    self.write(" ");
2047                    self.write_keyword("FROM ");
2048                    self.gen_expr(expr);
2049                    self.write(")");
2050                }
2051            }
2052            Expr::Interval { value, unit } => {
2053                self.write_keyword("INTERVAL ");
2054                self.gen_expr(value);
2055                if let Some(unit) = unit {
2056                    self.write(" ");
2057                    self.gen_datetime_field(unit);
2058                }
2059            }
2060            Expr::ArrayLiteral(items) => {
2061                self.write_keyword("ARRAY[");
2062                self.gen_expr_list(items);
2063                self.write("]");
2064            }
2065            Expr::Tuple(items) => {
2066                self.write("(");
2067                self.gen_expr_list(items);
2068                self.write(")");
2069            }
2070            Expr::Coalesce(items) => {
2071                self.write_keyword("COALESCE(");
2072                self.gen_expr_list(items);
2073                self.write(")");
2074            }
2075            Expr::If {
2076                condition,
2077                true_val,
2078                false_val,
2079            } => {
2080                self.write_keyword("IF(");
2081                self.gen_expr(condition);
2082                self.write(", ");
2083                self.gen_expr(true_val);
2084                if let Some(fv) = false_val {
2085                    self.write(", ");
2086                    self.gen_expr(fv);
2087                }
2088                self.write(")");
2089            }
2090            Expr::NullIf { expr, r#else } => {
2091                self.write_keyword("NULLIF(");
2092                self.gen_expr(expr);
2093                self.write(", ");
2094                self.gen_expr(r#else);
2095                self.write(")");
2096            }
2097            Expr::Collate { expr, collation } => {
2098                self.gen_expr(expr);
2099                self.write(" ");
2100                self.write_keyword("COLLATE ");
2101                self.write(collation);
2102            }
2103            Expr::Parameter(p) => self.write(p),
2104            Expr::TypeExpr(dt) => self.gen_data_type(dt),
2105            Expr::QualifiedWildcard { table } => {
2106                self.write(table);
2107                self.write(".*");
2108            }
2109            Expr::Alias { expr, name } => {
2110                self.gen_expr(expr);
2111                self.write(" ");
2112                self.write_keyword("AS ");
2113                self.write(name);
2114            }
2115            Expr::ArrayIndex { expr, index } => {
2116                self.gen_expr(expr);
2117                self.write("[");
2118                self.gen_expr(index);
2119                self.write("]");
2120            }
2121            Expr::JsonAccess {
2122                expr,
2123                path,
2124                as_text,
2125            } => {
2126                self.gen_expr(expr);
2127                if *as_text {
2128                    self.write("->>");
2129                } else {
2130                    self.write("->");
2131                }
2132                self.gen_expr(path);
2133            }
2134            Expr::Lambda { params, body } => {
2135                if params.len() == 1 {
2136                    self.write(&params[0]);
2137                } else {
2138                    self.write("(");
2139                    self.write(&params.join(", "));
2140                    self.write(")");
2141                }
2142                self.write(" -> ");
2143                self.gen_expr(body);
2144            }
2145            Expr::TypedFunction { func, filter, over } => {
2146                self.gen_typed_function(func);
2147
2148                if let Some(filter_expr) = filter {
2149                    self.write(" ");
2150                    self.write_keyword("FILTER (WHERE ");
2151                    self.gen_expr(filter_expr);
2152                    self.write(")");
2153                }
2154                if let Some(spec) = over {
2155                    self.write(" ");
2156                    self.write_keyword("OVER ");
2157                    if let Some(wref) = &spec.window_ref {
2158                        if spec.partition_by.is_empty()
2159                            && spec.order_by.is_empty()
2160                            && spec.frame.is_none()
2161                        {
2162                            self.write(wref);
2163                        } else {
2164                            self.write("(");
2165                            self.gen_window_spec(spec);
2166                            self.write(")");
2167                        }
2168                    } else {
2169                        self.write("(");
2170                        self.gen_window_spec(spec);
2171                        self.write(")");
2172                    }
2173                }
2174            }
2175            Expr::Commented { expr, comments } => {
2176                for comment in comments {
2177                    let normalized = self.normalize_comment(comment);
2178                    self.write(&normalized);
2179                    self.write(" ");
2180                }
2181                self.gen_expr(expr);
2182            }
2183        }
2184    }
2185
2186    fn gen_window_spec(&mut self, spec: &WindowSpec) {
2187        if let Some(wref) = &spec.window_ref {
2188            self.write(wref);
2189            if !spec.partition_by.is_empty() || !spec.order_by.is_empty() || spec.frame.is_some() {
2190                self.write(" ");
2191            }
2192        }
2193        if !spec.partition_by.is_empty() {
2194            self.write_keyword("PARTITION BY ");
2195            self.gen_expr_list(&spec.partition_by);
2196        }
2197        if !spec.order_by.is_empty() {
2198            if !spec.partition_by.is_empty() {
2199                self.write(" ");
2200            }
2201            self.write_keyword("ORDER BY ");
2202            for (i, item) in spec.order_by.iter().enumerate() {
2203                if i > 0 {
2204                    self.write(", ");
2205                }
2206                self.gen_expr(&item.expr);
2207                if !item.ascending {
2208                    self.write(" ");
2209                    self.write_keyword("DESC");
2210                }
2211                if let Some(nulls_first) = item.nulls_first {
2212                    if nulls_first {
2213                        self.write(" ");
2214                        self.write_keyword("NULLS FIRST");
2215                    } else {
2216                        self.write(" ");
2217                        self.write_keyword("NULLS LAST");
2218                    }
2219                }
2220            }
2221        }
2222        if let Some(frame) = &spec.frame {
2223            self.write(" ");
2224            self.gen_window_frame(frame);
2225        }
2226    }
2227
2228    fn gen_window_frame(&mut self, frame: &WindowFrame) {
2229        match frame.kind {
2230            WindowFrameKind::Rows => self.write_keyword("ROWS "),
2231            WindowFrameKind::Range => self.write_keyword("RANGE "),
2232            WindowFrameKind::Groups => self.write_keyword("GROUPS "),
2233        }
2234        if let Some(end) = &frame.end {
2235            self.write_keyword("BETWEEN ");
2236            self.gen_window_frame_bound(&frame.start);
2237            self.write(" ");
2238            self.write_keyword("AND ");
2239            self.gen_window_frame_bound(end);
2240        } else {
2241            self.gen_window_frame_bound(&frame.start);
2242        }
2243    }
2244
2245    fn gen_window_frame_bound(&mut self, bound: &WindowFrameBound) {
2246        match bound {
2247            WindowFrameBound::CurrentRow => self.write_keyword("CURRENT ROW"),
2248            WindowFrameBound::Preceding(None) => self.write_keyword("UNBOUNDED PRECEDING"),
2249            WindowFrameBound::Preceding(Some(n)) => {
2250                self.gen_expr(n);
2251                self.write(" ");
2252                self.write_keyword("PRECEDING");
2253            }
2254            WindowFrameBound::Following(None) => self.write_keyword("UNBOUNDED FOLLOWING"),
2255            WindowFrameBound::Following(Some(n)) => {
2256                self.gen_expr(n);
2257                self.write(" ");
2258                self.write_keyword("FOLLOWING");
2259            }
2260        }
2261    }
2262
2263    fn gen_datetime_field(&mut self, field: &DateTimeField) {
2264        let name = match field {
2265            DateTimeField::Year => "YEAR",
2266            DateTimeField::Quarter => "QUARTER",
2267            DateTimeField::Month => "MONTH",
2268            DateTimeField::Week => "WEEK",
2269            DateTimeField::Day => "DAY",
2270            DateTimeField::DayOfWeek => "DOW",
2271            DateTimeField::DayOfYear => "DOY",
2272            DateTimeField::Hour => "HOUR",
2273            DateTimeField::Minute => "MINUTE",
2274            DateTimeField::Second => "SECOND",
2275            DateTimeField::Millisecond => "MILLISECOND",
2276            DateTimeField::Microsecond => "MICROSECOND",
2277            DateTimeField::Nanosecond => "NANOSECOND",
2278            DateTimeField::Epoch => "EPOCH",
2279            DateTimeField::Timezone => "TIMEZONE",
2280            DateTimeField::TimezoneHour => "TIMEZONE_HOUR",
2281            DateTimeField::TimezoneMinute => "TIMEZONE_MINUTE",
2282        };
2283        self.write(name);
2284    }
2285
2286    /// Generate SQL for a typed function expression.
2287    fn gen_typed_function(&mut self, func: &TypedFunction) {
2288        let dialect = self.dialect;
2289        let is_tsql = matches!(dialect, Some(Dialect::Tsql) | Some(Dialect::Fabric));
2290        let is_mysql = matches!(
2291            dialect,
2292            Some(Dialect::Mysql)
2293                | Some(Dialect::SingleStore)
2294                | Some(Dialect::Doris)
2295                | Some(Dialect::StarRocks)
2296        );
2297        let is_bigquery = matches!(dialect, Some(Dialect::BigQuery));
2298        let is_snowflake = matches!(dialect, Some(Dialect::Snowflake));
2299        let is_oracle = matches!(dialect, Some(Dialect::Oracle));
2300        let is_hive_family = matches!(
2301            dialect,
2302            Some(Dialect::Hive) | Some(Dialect::Spark) | Some(Dialect::Databricks)
2303        );
2304
2305        match func {
2306            // ── Date/Time ──────────────────────────────────────────────
2307            TypedFunction::DateAdd {
2308                expr,
2309                interval,
2310                unit,
2311            } => {
2312                if is_tsql || is_snowflake {
2313                    self.write_keyword("DATEADD(");
2314                    if let Some(u) = unit {
2315                        self.gen_datetime_field(u);
2316                    } else {
2317                        self.write_keyword("DAY");
2318                    }
2319                    self.write(", ");
2320                    self.gen_expr(interval);
2321                    self.write(", ");
2322                    self.gen_expr(expr);
2323                    self.write(")");
2324                } else if is_bigquery {
2325                    self.write_keyword("DATE_ADD(");
2326                    self.gen_expr(expr);
2327                    self.write(", ");
2328                    self.write_keyword("INTERVAL ");
2329                    self.gen_expr(interval);
2330                    self.write(" ");
2331                    if let Some(u) = unit {
2332                        self.gen_datetime_field(u);
2333                    } else {
2334                        self.write_keyword("DAY");
2335                    }
2336                    self.write(")");
2337                } else {
2338                    self.write_keyword("DATE_ADD(");
2339                    self.gen_expr(expr);
2340                    self.write(", ");
2341                    self.gen_expr(interval);
2342                    if let Some(u) = unit {
2343                        self.write(", ");
2344                        self.gen_datetime_field(u);
2345                    }
2346                    self.write(")");
2347                }
2348            }
2349            TypedFunction::DateDiff { start, end, unit } => {
2350                if is_tsql || is_snowflake {
2351                    self.write_keyword("DATEDIFF(");
2352                    if let Some(u) = unit {
2353                        self.gen_datetime_field(u);
2354                    } else {
2355                        self.write_keyword("DAY");
2356                    }
2357                    self.write(", ");
2358                    self.gen_expr(start);
2359                    self.write(", ");
2360                    self.gen_expr(end);
2361                    self.write(")");
2362                } else if is_bigquery {
2363                    self.write_keyword("DATE_DIFF(");
2364                    self.gen_expr(end);
2365                    self.write(", ");
2366                    self.gen_expr(start);
2367                    self.write(", ");
2368                    if let Some(u) = unit {
2369                        self.gen_datetime_field(u);
2370                    } else {
2371                        self.write_keyword("DAY");
2372                    }
2373                    self.write(")");
2374                } else {
2375                    self.write_keyword("DATEDIFF(");
2376                    self.gen_expr(start);
2377                    self.write(", ");
2378                    self.gen_expr(end);
2379                    if let Some(u) = unit {
2380                        self.write(", ");
2381                        self.gen_datetime_field(u);
2382                    }
2383                    self.write(")");
2384                }
2385            }
2386            TypedFunction::DateTrunc { unit, expr } => {
2387                if is_tsql {
2388                    self.write_keyword("DATETRUNC(");
2389                    self.gen_datetime_field(unit);
2390                    self.write(", ");
2391                    self.gen_expr(expr);
2392                    self.write(")");
2393                } else if is_oracle {
2394                    self.write_keyword("TRUNC(");
2395                    self.gen_expr(expr);
2396                    self.write(", '");
2397                    self.gen_datetime_field(unit);
2398                    self.write("')");
2399                } else {
2400                    self.write_keyword("DATE_TRUNC(");
2401                    self.write("'");
2402                    self.gen_datetime_field(unit);
2403                    self.write("'");
2404                    self.write(", ");
2405                    self.gen_expr(expr);
2406                    self.write(")");
2407                }
2408            }
2409            TypedFunction::DateSub {
2410                expr,
2411                interval,
2412                unit,
2413            } => {
2414                if is_tsql || is_snowflake {
2415                    self.write_keyword("DATEADD(");
2416                    if let Some(u) = unit {
2417                        self.gen_datetime_field(u);
2418                    } else {
2419                        self.write_keyword("DAY");
2420                    }
2421                    self.write(", -(");
2422                    self.gen_expr(interval);
2423                    self.write("), ");
2424                    self.gen_expr(expr);
2425                    self.write(")");
2426                } else if is_bigquery {
2427                    self.write_keyword("DATE_SUB(");
2428                    self.gen_expr(expr);
2429                    self.write(", ");
2430                    self.write_keyword("INTERVAL ");
2431                    self.gen_expr(interval);
2432                    self.write(" ");
2433                    if let Some(u) = unit {
2434                        self.gen_datetime_field(u);
2435                    } else {
2436                        self.write_keyword("DAY");
2437                    }
2438                    self.write(")");
2439                } else {
2440                    self.write_keyword("DATE_SUB(");
2441                    self.gen_expr(expr);
2442                    self.write(", ");
2443                    self.gen_expr(interval);
2444                    if let Some(u) = unit {
2445                        self.write(", ");
2446                        self.gen_datetime_field(u);
2447                    }
2448                    self.write(")");
2449                }
2450            }
2451            TypedFunction::CurrentDate => {
2452                if is_tsql {
2453                    self.write_keyword("CAST(GETDATE() AS DATE)");
2454                } else if is_mysql || is_hive_family {
2455                    self.write_keyword("CURRENT_DATE()");
2456                } else {
2457                    self.write_keyword("CURRENT_DATE");
2458                }
2459            }
2460            TypedFunction::CurrentTimestamp => {
2461                if is_tsql {
2462                    self.write_keyword("GETDATE()");
2463                } else if is_mysql
2464                    || matches!(
2465                        dialect,
2466                        Some(Dialect::Postgres)
2467                            | Some(Dialect::DuckDb)
2468                            | Some(Dialect::Sqlite)
2469                            | Some(Dialect::Redshift)
2470                    )
2471                {
2472                    self.write_keyword("NOW()");
2473                } else {
2474                    self.write_keyword("CURRENT_TIMESTAMP()");
2475                }
2476            }
2477            TypedFunction::StrToTime { expr, format } => {
2478                if is_mysql {
2479                    self.write_keyword("STR_TO_DATE(");
2480                } else if is_bigquery {
2481                    self.write_keyword("PARSE_TIMESTAMP(");
2482                } else {
2483                    self.write_keyword("TO_TIMESTAMP(");
2484                }
2485                self.gen_expr(expr);
2486                self.write(", ");
2487                self.gen_expr(format);
2488                self.write(")");
2489            }
2490            TypedFunction::TimeToStr { expr, format } => {
2491                if is_mysql || is_hive_family {
2492                    self.write_keyword("DATE_FORMAT(");
2493                } else if is_bigquery {
2494                    self.write_keyword("FORMAT_TIMESTAMP(");
2495                } else if is_tsql {
2496                    self.write_keyword("FORMAT(");
2497                } else {
2498                    self.write_keyword("TO_CHAR(");
2499                }
2500                self.gen_expr(expr);
2501                self.write(", ");
2502                self.gen_expr(format);
2503                self.write(")");
2504            }
2505            TypedFunction::TsOrDsToDate { expr } => {
2506                if is_mysql {
2507                    self.write_keyword("DATE(");
2508                    self.gen_expr(expr);
2509                    self.write(")");
2510                } else {
2511                    self.write_keyword("CAST(");
2512                    self.gen_expr(expr);
2513                    self.write(" ");
2514                    self.write_keyword("AS DATE)");
2515                }
2516            }
2517            TypedFunction::Year { expr } => {
2518                if is_tsql {
2519                    self.write_keyword("YEAR(");
2520                    self.gen_expr(expr);
2521                    self.write(")");
2522                } else {
2523                    self.write_keyword("EXTRACT(YEAR FROM ");
2524                    self.gen_expr(expr);
2525                    self.write(")");
2526                }
2527            }
2528            TypedFunction::Month { expr } => {
2529                if is_tsql {
2530                    self.write_keyword("MONTH(");
2531                    self.gen_expr(expr);
2532                    self.write(")");
2533                } else {
2534                    self.write_keyword("EXTRACT(MONTH FROM ");
2535                    self.gen_expr(expr);
2536                    self.write(")");
2537                }
2538            }
2539            TypedFunction::Day { expr } => {
2540                if is_tsql {
2541                    self.write_keyword("DAY(");
2542                    self.gen_expr(expr);
2543                    self.write(")");
2544                } else {
2545                    self.write_keyword("EXTRACT(DAY FROM ");
2546                    self.gen_expr(expr);
2547                    self.write(")");
2548                }
2549            }
2550
2551            // ── String ─────────────────────────────────────────────────
2552            TypedFunction::Trim {
2553                expr,
2554                trim_type,
2555                trim_chars,
2556            } => {
2557                self.write_keyword("TRIM(");
2558                match trim_type {
2559                    TrimType::Leading => self.write_keyword("LEADING "),
2560                    TrimType::Trailing => self.write_keyword("TRAILING "),
2561                    TrimType::Both => {} // BOTH is default
2562                }
2563                if let Some(chars) = trim_chars {
2564                    self.gen_expr(chars);
2565                    self.write(" ");
2566                    self.write_keyword("FROM ");
2567                }
2568                self.gen_expr(expr);
2569                self.write(")");
2570            }
2571            TypedFunction::Substring {
2572                expr,
2573                start,
2574                length,
2575            } => {
2576                let name = if is_oracle
2577                    || is_hive_family
2578                    || is_mysql
2579                    || matches!(
2580                        dialect,
2581                        Some(Dialect::Sqlite)
2582                            | Some(Dialect::Doris)
2583                            | Some(Dialect::SingleStore)
2584                            | Some(Dialect::StarRocks)
2585                    ) {
2586                    "SUBSTR"
2587                } else {
2588                    "SUBSTRING"
2589                };
2590                self.write_keyword(name);
2591                self.write("(");
2592                self.gen_expr(expr);
2593                self.write(", ");
2594                self.gen_expr(start);
2595                if let Some(l) = length {
2596                    self.write(", ");
2597                    self.gen_expr(l);
2598                }
2599                self.write(")");
2600            }
2601            TypedFunction::Upper { expr } => {
2602                self.write_keyword("UPPER(");
2603                self.gen_expr(expr);
2604                self.write(")");
2605            }
2606            TypedFunction::Lower { expr } => {
2607                self.write_keyword("LOWER(");
2608                self.gen_expr(expr);
2609                self.write(")");
2610            }
2611            TypedFunction::RegexpLike {
2612                expr,
2613                pattern,
2614                flags,
2615            } => {
2616                self.write_keyword("REGEXP_LIKE(");
2617                self.gen_expr(expr);
2618                self.write(", ");
2619                self.gen_expr(pattern);
2620                if let Some(f) = flags {
2621                    self.write(", ");
2622                    self.gen_expr(f);
2623                }
2624                self.write(")");
2625            }
2626            TypedFunction::RegexpExtract {
2627                expr,
2628                pattern,
2629                group_index,
2630            } => {
2631                if is_bigquery || is_hive_family {
2632                    self.write_keyword("REGEXP_EXTRACT(");
2633                } else {
2634                    self.write_keyword("REGEXP_SUBSTR(");
2635                }
2636                self.gen_expr(expr);
2637                self.write(", ");
2638                self.gen_expr(pattern);
2639                if let Some(g) = group_index {
2640                    self.write(", ");
2641                    self.gen_expr(g);
2642                }
2643                self.write(")");
2644            }
2645            TypedFunction::RegexpReplace {
2646                expr,
2647                pattern,
2648                replacement,
2649                flags,
2650            } => {
2651                self.write_keyword("REGEXP_REPLACE(");
2652                self.gen_expr(expr);
2653                self.write(", ");
2654                self.gen_expr(pattern);
2655                self.write(", ");
2656                self.gen_expr(replacement);
2657                if let Some(f) = flags {
2658                    self.write(", ");
2659                    self.gen_expr(f);
2660                }
2661                self.write(")");
2662            }
2663            TypedFunction::ConcatWs { separator, exprs } => {
2664                self.write_keyword("CONCAT_WS(");
2665                self.gen_expr(separator);
2666                for e in exprs {
2667                    self.write(", ");
2668                    self.gen_expr(e);
2669                }
2670                self.write(")");
2671            }
2672            TypedFunction::Split { expr, delimiter } => {
2673                if is_tsql {
2674                    self.write_keyword("STRING_SPLIT(");
2675                } else {
2676                    self.write_keyword("SPLIT(");
2677                }
2678                self.gen_expr(expr);
2679                self.write(", ");
2680                self.gen_expr(delimiter);
2681                self.write(")");
2682            }
2683            TypedFunction::Initcap { expr } => {
2684                self.write_keyword("INITCAP(");
2685                self.gen_expr(expr);
2686                self.write(")");
2687            }
2688            TypedFunction::Length { expr } => {
2689                let name = if is_tsql || is_bigquery || is_snowflake {
2690                    "LEN"
2691                } else {
2692                    "LENGTH"
2693                };
2694                self.write_keyword(name);
2695                self.write("(");
2696                self.gen_expr(expr);
2697                self.write(")");
2698            }
2699            TypedFunction::Replace { expr, from, to } => {
2700                self.write_keyword("REPLACE(");
2701                self.gen_expr(expr);
2702                self.write(", ");
2703                self.gen_expr(from);
2704                self.write(", ");
2705                self.gen_expr(to);
2706                self.write(")");
2707            }
2708            TypedFunction::Reverse { expr } => {
2709                self.write_keyword("REVERSE(");
2710                self.gen_expr(expr);
2711                self.write(")");
2712            }
2713            TypedFunction::Left { expr, n } => {
2714                self.write_keyword("LEFT(");
2715                self.gen_expr(expr);
2716                self.write(", ");
2717                self.gen_expr(n);
2718                self.write(")");
2719            }
2720            TypedFunction::Right { expr, n } => {
2721                self.write_keyword("RIGHT(");
2722                self.gen_expr(expr);
2723                self.write(", ");
2724                self.gen_expr(n);
2725                self.write(")");
2726            }
2727            TypedFunction::Lpad { expr, length, pad } => {
2728                self.write_keyword("LPAD(");
2729                self.gen_expr(expr);
2730                self.write(", ");
2731                self.gen_expr(length);
2732                if let Some(p) = pad {
2733                    self.write(", ");
2734                    self.gen_expr(p);
2735                }
2736                self.write(")");
2737            }
2738            TypedFunction::Rpad { expr, length, pad } => {
2739                self.write_keyword("RPAD(");
2740                self.gen_expr(expr);
2741                self.write(", ");
2742                self.gen_expr(length);
2743                if let Some(p) = pad {
2744                    self.write(", ");
2745                    self.gen_expr(p);
2746                }
2747                self.write(")");
2748            }
2749
2750            // ── Aggregate ──────────────────────────────────────────────
2751            TypedFunction::Count { expr, distinct } => {
2752                self.write_keyword("COUNT(");
2753                if *distinct {
2754                    self.write_keyword("DISTINCT ");
2755                }
2756                self.gen_expr(expr);
2757                self.write(")");
2758            }
2759            TypedFunction::Sum { expr, distinct } => {
2760                self.write_keyword("SUM(");
2761                if *distinct {
2762                    self.write_keyword("DISTINCT ");
2763                }
2764                self.gen_expr(expr);
2765                self.write(")");
2766            }
2767            TypedFunction::Avg { expr, distinct } => {
2768                self.write_keyword("AVG(");
2769                if *distinct {
2770                    self.write_keyword("DISTINCT ");
2771                }
2772                self.gen_expr(expr);
2773                self.write(")");
2774            }
2775            TypedFunction::Min { expr } => {
2776                self.write_keyword("MIN(");
2777                self.gen_expr(expr);
2778                self.write(")");
2779            }
2780            TypedFunction::Max { expr } => {
2781                self.write_keyword("MAX(");
2782                self.gen_expr(expr);
2783                self.write(")");
2784            }
2785            TypedFunction::ArrayAgg { expr, distinct } => {
2786                let name = if matches!(dialect, Some(Dialect::DuckDb)) {
2787                    "LIST"
2788                } else if is_hive_family {
2789                    "COLLECT_LIST"
2790                } else {
2791                    "ARRAY_AGG"
2792                };
2793                self.write_keyword(name);
2794                self.write("(");
2795                if *distinct {
2796                    self.write_keyword("DISTINCT ");
2797                }
2798                self.gen_expr(expr);
2799                self.write(")");
2800            }
2801            TypedFunction::ApproxDistinct { expr } => {
2802                let name = if is_hive_family
2803                    || matches!(
2804                        dialect,
2805                        Some(Dialect::Presto) | Some(Dialect::Trino) | Some(Dialect::Athena)
2806                    ) {
2807                    "APPROX_DISTINCT"
2808                } else {
2809                    "APPROX_COUNT_DISTINCT"
2810                };
2811                self.write_keyword(name);
2812                self.write("(");
2813                self.gen_expr(expr);
2814                self.write(")");
2815            }
2816            TypedFunction::Variance { expr } => {
2817                let name = if is_tsql || is_oracle {
2818                    "VAR"
2819                } else {
2820                    "VARIANCE"
2821                };
2822                self.write_keyword(name);
2823                self.write("(");
2824                self.gen_expr(expr);
2825                self.write(")");
2826            }
2827            TypedFunction::Stddev { expr } => {
2828                self.write_keyword("STDDEV(");
2829                self.gen_expr(expr);
2830                self.write(")");
2831            }
2832            TypedFunction::GroupConcat {
2833                exprs,
2834                separator,
2835                order_by,
2836                distinct,
2837            } => {
2838                self.gen_group_concat(exprs, separator.as_deref(), order_by, *distinct);
2839            }
2840
2841            // ── Array ──────────────────────────────────────────────────
2842            TypedFunction::ArrayConcat { arrays } => {
2843                let name = if matches!(
2844                    dialect,
2845                    Some(Dialect::Postgres) | Some(Dialect::Redshift) | Some(Dialect::DuckDb)
2846                ) {
2847                    "ARRAY_CAT"
2848                } else {
2849                    "ARRAY_CONCAT"
2850                };
2851                self.write_keyword(name);
2852                self.write("(");
2853                self.gen_expr_list(arrays);
2854                self.write(")");
2855            }
2856            TypedFunction::ArrayContains { array, element } => {
2857                self.write_keyword("ARRAY_CONTAINS(");
2858                self.gen_expr(array);
2859                self.write(", ");
2860                self.gen_expr(element);
2861                self.write(")");
2862            }
2863            TypedFunction::ArraySize { expr } => {
2864                let name = if matches!(dialect, Some(Dialect::Postgres) | Some(Dialect::Redshift)) {
2865                    "ARRAY_LENGTH"
2866                } else if is_hive_family {
2867                    "SIZE"
2868                } else {
2869                    "ARRAY_SIZE"
2870                };
2871                self.write_keyword(name);
2872                self.write("(");
2873                self.gen_expr(expr);
2874                self.write(")");
2875            }
2876            TypedFunction::Explode { expr } => {
2877                self.write_keyword("EXPLODE(");
2878                self.gen_expr(expr);
2879                self.write(")");
2880            }
2881            TypedFunction::GenerateSeries { start, stop, step } => {
2882                self.write_keyword("GENERATE_SERIES(");
2883                self.gen_expr(start);
2884                self.write(", ");
2885                self.gen_expr(stop);
2886                if let Some(s) = step {
2887                    self.write(", ");
2888                    self.gen_expr(s);
2889                }
2890                self.write(")");
2891            }
2892            TypedFunction::Flatten { expr } => {
2893                self.write_keyword("FLATTEN(");
2894                self.gen_expr(expr);
2895                self.write(")");
2896            }
2897
2898            // ── JSON ───────────────────────────────────────────────────
2899            TypedFunction::JSONExtract { expr, path } => {
2900                if is_tsql {
2901                    self.write_keyword("JSON_VALUE(");
2902                } else {
2903                    self.write_keyword("JSON_EXTRACT(");
2904                }
2905                self.gen_expr(expr);
2906                self.write(", ");
2907                self.gen_expr(path);
2908                self.write(")");
2909            }
2910            TypedFunction::JSONExtractScalar { expr, path } => {
2911                if is_bigquery {
2912                    self.write_keyword("JSON_EXTRACT_SCALAR(");
2913                } else if is_tsql {
2914                    self.write_keyword("JSON_VALUE(");
2915                } else {
2916                    self.write_keyword("JSON_EXTRACT_SCALAR(");
2917                }
2918                self.gen_expr(expr);
2919                self.write(", ");
2920                self.gen_expr(path);
2921                self.write(")");
2922            }
2923            TypedFunction::ParseJSON { expr } => {
2924                if is_snowflake {
2925                    self.write_keyword("PARSE_JSON(");
2926                } else if is_bigquery {
2927                    self.write_keyword("JSON_PARSE(");
2928                } else {
2929                    self.write_keyword("PARSE_JSON(");
2930                }
2931                self.gen_expr(expr);
2932                self.write(")");
2933            }
2934            TypedFunction::JSONFormat { expr } => {
2935                if is_bigquery {
2936                    self.write_keyword("TO_JSON_STRING(");
2937                } else {
2938                    self.write_keyword("JSON_FORMAT(");
2939                }
2940                self.gen_expr(expr);
2941                self.write(")");
2942            }
2943
2944            // ── Window ─────────────────────────────────────────────────
2945            TypedFunction::RowNumber => self.write_keyword("ROW_NUMBER()"),
2946            TypedFunction::Rank => self.write_keyword("RANK()"),
2947            TypedFunction::DenseRank => self.write_keyword("DENSE_RANK()"),
2948            TypedFunction::NTile { n } => {
2949                self.write_keyword("NTILE(");
2950                self.gen_expr(n);
2951                self.write(")");
2952            }
2953            TypedFunction::Lead {
2954                expr,
2955                offset,
2956                default,
2957            } => {
2958                self.write_keyword("LEAD(");
2959                self.gen_expr(expr);
2960                if let Some(o) = offset {
2961                    self.write(", ");
2962                    self.gen_expr(o);
2963                }
2964                if let Some(d) = default {
2965                    self.write(", ");
2966                    self.gen_expr(d);
2967                }
2968                self.write(")");
2969            }
2970            TypedFunction::Lag {
2971                expr,
2972                offset,
2973                default,
2974            } => {
2975                self.write_keyword("LAG(");
2976                self.gen_expr(expr);
2977                if let Some(o) = offset {
2978                    self.write(", ");
2979                    self.gen_expr(o);
2980                }
2981                if let Some(d) = default {
2982                    self.write(", ");
2983                    self.gen_expr(d);
2984                }
2985                self.write(")");
2986            }
2987            TypedFunction::FirstValue { expr } => {
2988                self.write_keyword("FIRST_VALUE(");
2989                self.gen_expr(expr);
2990                self.write(")");
2991            }
2992            TypedFunction::LastValue { expr } => {
2993                self.write_keyword("LAST_VALUE(");
2994                self.gen_expr(expr);
2995                self.write(")");
2996            }
2997
2998            // ── Math ───────────────────────────────────────────────────
2999            TypedFunction::Abs { expr } => {
3000                self.write_keyword("ABS(");
3001                self.gen_expr(expr);
3002                self.write(")");
3003            }
3004            TypedFunction::Ceil { expr } => {
3005                let name = if is_tsql { "CEILING" } else { "CEIL" };
3006                self.write_keyword(name);
3007                self.write("(");
3008                self.gen_expr(expr);
3009                self.write(")");
3010            }
3011            TypedFunction::Floor { expr } => {
3012                self.write_keyword("FLOOR(");
3013                self.gen_expr(expr);
3014                self.write(")");
3015            }
3016            TypedFunction::Round { expr, decimals } => {
3017                self.write_keyword("ROUND(");
3018                self.gen_expr(expr);
3019                if let Some(d) = decimals {
3020                    self.write(", ");
3021                    self.gen_expr(d);
3022                }
3023                self.write(")");
3024            }
3025            TypedFunction::Log { expr, base } => {
3026                if let Some(b) = base {
3027                    self.write_keyword("LOG(");
3028                    if matches!(dialect, Some(Dialect::Postgres) | Some(Dialect::DuckDb)) {
3029                        // Postgres: LOG(base, expr)
3030                        self.gen_expr(b);
3031                        self.write(", ");
3032                        self.gen_expr(expr);
3033                    } else {
3034                        // Most: LOG(expr, base)
3035                        self.gen_expr(expr);
3036                        self.write(", ");
3037                        self.gen_expr(b);
3038                    }
3039                    self.write(")");
3040                } else {
3041                    // LOG(expr) — ln in Postgres, log10 in most others
3042                    self.write_keyword("LOG(");
3043                    self.gen_expr(expr);
3044                    self.write(")");
3045                }
3046            }
3047            TypedFunction::Ln { expr } => {
3048                self.write_keyword("LN(");
3049                self.gen_expr(expr);
3050                self.write(")");
3051            }
3052            TypedFunction::Pow { base, exponent } => {
3053                let name = if is_tsql || is_oracle { "POWER" } else { "POW" };
3054                self.write_keyword(name);
3055                self.write("(");
3056                self.gen_expr(base);
3057                self.write(", ");
3058                self.gen_expr(exponent);
3059                self.write(")");
3060            }
3061            TypedFunction::Sqrt { expr } => {
3062                self.write_keyword("SQRT(");
3063                self.gen_expr(expr);
3064                self.write(")");
3065            }
3066            TypedFunction::Greatest { exprs } => {
3067                self.write_keyword("GREATEST(");
3068                self.gen_expr_list(exprs);
3069                self.write(")");
3070            }
3071            TypedFunction::Least { exprs } => {
3072                self.write_keyword("LEAST(");
3073                self.gen_expr_list(exprs);
3074                self.write(")");
3075            }
3076            TypedFunction::Mod { left, right } => {
3077                self.write_keyword("MOD(");
3078                self.gen_expr(left);
3079                self.write(", ");
3080                self.gen_expr(right);
3081                self.write(")");
3082            }
3083
3084            // ── Conversion ─────────────────────────────────────────────
3085            TypedFunction::Hex { expr } => {
3086                let name = if matches!(
3087                    dialect,
3088                    Some(Dialect::Presto) | Some(Dialect::Trino) | Some(Dialect::Athena)
3089                ) {
3090                    "TO_HEX"
3091                } else {
3092                    "HEX"
3093                };
3094                self.write_keyword(name);
3095                self.write("(");
3096                self.gen_expr(expr);
3097                self.write(")");
3098            }
3099            TypedFunction::Unhex { expr } => {
3100                let name = if matches!(
3101                    dialect,
3102                    Some(Dialect::Presto) | Some(Dialect::Trino) | Some(Dialect::Athena)
3103                ) {
3104                    "FROM_HEX"
3105                } else {
3106                    "UNHEX"
3107                };
3108                self.write_keyword(name);
3109                self.write("(");
3110                self.gen_expr(expr);
3111                self.write(")");
3112            }
3113            TypedFunction::Md5 { expr } => {
3114                self.write_keyword("MD5(");
3115                self.gen_expr(expr);
3116                self.write(")");
3117            }
3118            TypedFunction::Sha { expr } => {
3119                let name = if is_mysql { "SHA1" } else { "SHA" };
3120                self.write_keyword(name);
3121                self.write("(");
3122                self.gen_expr(expr);
3123                self.write(")");
3124            }
3125            TypedFunction::Sha2 { expr, bit_length } => {
3126                self.write_keyword("SHA2(");
3127                self.gen_expr(expr);
3128                self.write(", ");
3129                self.gen_expr(bit_length);
3130                self.write(")");
3131            }
3132        }
3133    }
3134
3135    /// Emit a `GROUP_CONCAT` / `STRING_AGG` / `LISTAGG` expression
3136    /// appropriately for the active dialect.
3137    fn gen_group_concat(
3138        &mut self,
3139        exprs: &[Expr],
3140        separator: Option<&Expr>,
3141        order_by: &[OrderByItem],
3142        distinct: bool,
3143    ) {
3144        let dialect = self.dialect;
3145        let is_sqlite = matches!(dialect, Some(Dialect::Sqlite));
3146        let is_string_agg = matches!(
3147            dialect,
3148            Some(Dialect::Postgres)
3149                | Some(Dialect::Redshift)
3150                | Some(Dialect::BigQuery)
3151                | Some(Dialect::Tsql)
3152                | Some(Dialect::Fabric)
3153                | Some(Dialect::DuckDb)
3154        );
3155        let is_listagg = matches!(dialect, Some(Dialect::Oracle) | Some(Dialect::Snowflake));
3156
3157        if is_string_agg {
3158            // STRING_AGG(expr, sep [ORDER BY ...])
3159            self.write_keyword("STRING_AGG(");
3160            if distinct {
3161                self.write_keyword("DISTINCT ");
3162            }
3163            self.gen_group_concat_exprs(exprs);
3164            self.write(", ");
3165            match separator {
3166                Some(s) => self.gen_expr(s),
3167                None => self.write("','"),
3168            }
3169            if !order_by.is_empty() {
3170                self.write(" ");
3171                self.gen_group_concat_order_by(order_by);
3172            }
3173            self.write(")");
3174        } else if is_listagg {
3175            // LISTAGG(expr, sep) WITHIN GROUP (ORDER BY ...)
3176            self.write_keyword("LISTAGG(");
3177            if distinct {
3178                self.write_keyword("DISTINCT ");
3179            }
3180            self.gen_group_concat_exprs(exprs);
3181            self.write(", ");
3182            match separator {
3183                Some(s) => self.gen_expr(s),
3184                None => self.write("','"),
3185            }
3186            self.write(")");
3187            if !order_by.is_empty() {
3188                self.write(" ");
3189                self.write_keyword("WITHIN GROUP");
3190                self.write(" (");
3191                self.gen_group_concat_order_by(order_by);
3192                self.write(")");
3193            }
3194        } else if is_sqlite {
3195            // SQLite: GROUP_CONCAT(expr[, sep]). No DISTINCT/ORDER BY support;
3196            // they are dropped on output since the target dialect lacks them.
3197            self.write_keyword("GROUP_CONCAT(");
3198            self.gen_group_concat_exprs(exprs);
3199            if let Some(s) = separator {
3200                self.write(", ");
3201                self.gen_expr(s);
3202            }
3203            self.write(")");
3204        } else {
3205            // MySQL family (and default): full GROUP_CONCAT grammar.
3206            self.write_keyword("GROUP_CONCAT(");
3207            if distinct {
3208                self.write_keyword("DISTINCT ");
3209            }
3210            self.gen_group_concat_exprs(exprs);
3211            if !order_by.is_empty() {
3212                self.write(" ");
3213                self.gen_group_concat_order_by(order_by);
3214            }
3215            if let Some(s) = separator {
3216                self.write(" ");
3217                self.write_keyword("SEPARATOR");
3218                self.write(" ");
3219                self.gen_expr(s);
3220            }
3221            self.write(")");
3222        }
3223    }
3224
3225    fn gen_group_concat_exprs(&mut self, exprs: &[Expr]) {
3226        for (i, e) in exprs.iter().enumerate() {
3227            if i > 0 {
3228                self.write(", ");
3229            }
3230            self.gen_expr(e);
3231        }
3232    }
3233
3234    fn gen_group_concat_order_by(&mut self, order_by: &[OrderByItem]) {
3235        self.write_keyword("ORDER BY");
3236        self.write(" ");
3237        for (i, item) in order_by.iter().enumerate() {
3238            if i > 0 {
3239                self.write(", ");
3240            }
3241            self.gen_expr(&item.expr);
3242            if !item.ascending {
3243                self.write(" ");
3244                self.write_keyword("DESC");
3245            }
3246            if let Some(nulls_first) = item.nulls_first {
3247                self.write(" ");
3248                self.write_keyword(if nulls_first {
3249                    "NULLS FIRST"
3250                } else {
3251                    "NULLS LAST"
3252                });
3253            }
3254        }
3255    }
3256}
3257
3258impl Default for Generator {
3259    fn default() -> Self {
3260        Self::new()
3261    }
3262}
3263
3264#[cfg(test)]
3265mod tests {
3266    use super::*;
3267    use crate::parser::Parser;
3268
3269    fn roundtrip(sql: &str) -> String {
3270        let stmt = Parser::new(sql).unwrap().parse_statement().unwrap();
3271        let mut g = Generator::new();
3272        g.generate(&stmt)
3273    }
3274
3275    #[test]
3276    fn test_select_roundtrip() {
3277        assert_eq!(roundtrip("SELECT a, b FROM t"), "SELECT a, b FROM t");
3278    }
3279
3280    #[test]
3281    fn test_select_where() {
3282        assert_eq!(
3283            roundtrip("SELECT x FROM t WHERE x > 10"),
3284            "SELECT x FROM t WHERE x > 10"
3285        );
3286    }
3287
3288    #[test]
3289    fn test_select_wildcard() {
3290        assert_eq!(roundtrip("SELECT * FROM users"), "SELECT * FROM users");
3291    }
3292
3293    #[test]
3294    fn test_insert_values() {
3295        assert_eq!(
3296            roundtrip("INSERT INTO t (a, b) VALUES (1, 'hello')"),
3297            "INSERT INTO t (a, b) VALUES (1, 'hello')"
3298        );
3299    }
3300
3301    #[test]
3302    fn test_delete() {
3303        assert_eq!(
3304            roundtrip("DELETE FROM users WHERE id = 1"),
3305            "DELETE FROM users WHERE id = 1"
3306        );
3307    }
3308
3309    #[test]
3310    fn test_join() {
3311        assert_eq!(
3312            roundtrip("SELECT a.id, b.name FROM a INNER JOIN b ON a.id = b.a_id"),
3313            "SELECT a.id, b.name FROM a INNER JOIN b ON a.id = b.a_id"
3314        );
3315    }
3316
3317    #[test]
3318    fn test_create_table() {
3319        assert_eq!(
3320            roundtrip("CREATE TABLE users (id INT NOT NULL, name VARCHAR(255), email TEXT)"),
3321            "CREATE TABLE users (id INT NOT NULL, name VARCHAR(255), email TEXT)"
3322        );
3323    }
3324
3325    #[test]
3326    fn test_cte_roundtrip() {
3327        let sql = "WITH cte AS (SELECT 1 AS x) SELECT x FROM cte";
3328        assert_eq!(
3329            roundtrip(sql),
3330            "WITH cte AS (SELECT 1 AS x) SELECT x FROM cte"
3331        );
3332    }
3333
3334    #[test]
3335    fn test_union_roundtrip() {
3336        let sql = "SELECT 1 UNION ALL SELECT 2";
3337        assert_eq!(roundtrip(sql), "SELECT 1 UNION ALL SELECT 2");
3338    }
3339
3340    #[test]
3341    fn test_cast_roundtrip() {
3342        assert_eq!(
3343            roundtrip("SELECT CAST(x AS INT) FROM t"),
3344            "SELECT CAST(x AS INT) FROM t"
3345        );
3346    }
3347
3348    #[test]
3349    fn test_exists_roundtrip() {
3350        assert_eq!(
3351            roundtrip("SELECT * FROM t WHERE EXISTS (SELECT 1 FROM t2)"),
3352            "SELECT * FROM t WHERE EXISTS (SELECT 1 FROM t2)"
3353        );
3354    }
3355
3356    #[test]
3357    fn test_extract_roundtrip() {
3358        assert_eq!(
3359            roundtrip("SELECT EXTRACT(YEAR FROM created_at) FROM t"),
3360            "SELECT EXTRACT(YEAR FROM created_at) FROM t"
3361        );
3362    }
3363
3364    #[test]
3365    fn test_window_function_roundtrip() {
3366        assert_eq!(
3367            roundtrip("SELECT ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) FROM emp"),
3368            "SELECT ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) FROM emp"
3369        );
3370    }
3371
3372    #[test]
3373    fn test_subquery_from_roundtrip() {
3374        assert_eq!(
3375            roundtrip("SELECT * FROM (SELECT 1 AS x) AS sub"),
3376            "SELECT * FROM (SELECT 1 AS x) AS sub"
3377        );
3378    }
3379
3380    #[test]
3381    fn test_in_subquery_roundtrip() {
3382        assert_eq!(
3383            roundtrip("SELECT * FROM t WHERE id IN (SELECT id FROM t2)"),
3384            "SELECT * FROM t WHERE id IN (SELECT id FROM t2)"
3385        );
3386    }
3387
3388    // ═══════════════════════════════════════════════════════════════
3389    // Pretty-print tests
3390    // ═══════════════════════════════════════════════════════════════
3391
3392    fn pretty_print(sql: &str) -> String {
3393        let stmt = Parser::new(sql).unwrap().parse_statement().unwrap();
3394        let mut g = Generator::pretty();
3395        g.generate(&stmt)
3396    }
3397
3398    #[test]
3399    fn test_pretty_simple_select() {
3400        assert_eq!(
3401            pretty_print("SELECT a, b, c FROM t"),
3402            "SELECT\n  a,\n  b,\n  c\nFROM\n  t"
3403        );
3404    }
3405
3406    #[test]
3407    fn test_pretty_select_where() {
3408        assert_eq!(
3409            pretty_print("SELECT a FROM t WHERE a > 1"),
3410            "SELECT\n  a\nFROM\n  t\nWHERE\n  a > 1"
3411        );
3412    }
3413
3414    #[test]
3415    fn test_pretty_select_group_by_having() {
3416        assert_eq!(
3417            pretty_print("SELECT a, COUNT(*) FROM t GROUP BY a HAVING COUNT(*) > 1"),
3418            "SELECT\n  a,\n  COUNT(*)\nFROM\n  t\nGROUP BY\n  a\nHAVING\n  COUNT(*) > 1"
3419        );
3420    }
3421
3422    #[test]
3423    fn test_pretty_select_order_by_limit() {
3424        assert_eq!(
3425            pretty_print("SELECT a FROM t ORDER BY a DESC LIMIT 10"),
3426            "SELECT\n  a\nFROM\n  t\nORDER BY\n  a DESC\nLIMIT 10"
3427        );
3428    }
3429
3430    #[test]
3431    fn test_pretty_join() {
3432        assert_eq!(
3433            pretty_print("SELECT a.id, b.name FROM a INNER JOIN b ON a.id = b.a_id"),
3434            "SELECT\n  a.id,\n  b.name\nFROM\n  a\nINNER JOIN\n  b\n  ON a.id = b.a_id"
3435        );
3436    }
3437
3438    #[test]
3439    fn test_pretty_cte() {
3440        assert_eq!(
3441            pretty_print("WITH cte AS (SELECT 1 AS x) SELECT x FROM cte"),
3442            "WITH cte AS (\n  SELECT\n    1 AS x\n)\nSELECT\n  x\nFROM\n  cte"
3443        );
3444    }
3445
3446    #[test]
3447    fn test_pretty_union() {
3448        assert_eq!(
3449            pretty_print("SELECT 1 UNION ALL SELECT 2"),
3450            "SELECT\n  1\nUNION ALL\nSELECT\n  2"
3451        );
3452    }
3453
3454    #[test]
3455    fn test_pretty_insert() {
3456        assert_eq!(
3457            pretty_print("INSERT INTO t (a, b) VALUES (1, 'hello'), (2, 'world')"),
3458            "INSERT INTO t (a, b)\nVALUES\n  (1, 'hello'),\n  (2, 'world')"
3459        );
3460    }
3461
3462    #[test]
3463    fn test_pretty_update() {
3464        assert_eq!(
3465            pretty_print("UPDATE t SET a = 1, b = 2 WHERE c = 3"),
3466            "UPDATE t\nSET\n  a = 1,\n  b = 2\nWHERE\n  c = 3"
3467        );
3468    }
3469
3470    #[test]
3471    fn test_pretty_delete() {
3472        assert_eq!(
3473            pretty_print("DELETE FROM t WHERE id = 1"),
3474            "DELETE FROM t\nWHERE\n  id = 1"
3475        );
3476    }
3477
3478    #[test]
3479    fn test_pretty_create_table() {
3480        assert_eq!(
3481            pretty_print("CREATE TABLE t (id INT NOT NULL, name VARCHAR(255), email TEXT)"),
3482            "CREATE TABLE t (\n  id INT NOT NULL,\n  name VARCHAR(255),\n  email TEXT\n)"
3483        );
3484    }
3485
3486    #[test]
3487    fn test_pretty_complex_query() {
3488        let sql = "SELECT a, SUM(b) FROM t1 INNER JOIN t2 ON t1.id = t2.id WHERE t1.x > 1 GROUP BY a HAVING SUM(b) > 10 ORDER BY a LIMIT 100";
3489        let expected = "SELECT\n  a,\n  SUM(b)\nFROM\n  t1\nINNER JOIN\n  t2\n  ON t1.id = t2.id\nWHERE\n  t1.x > 1\nGROUP BY\n  a\nHAVING\n  SUM(b) > 10\nORDER BY\n  a\nLIMIT 100";
3490        assert_eq!(pretty_print(sql), expected);
3491    }
3492
3493    #[test]
3494    fn test_pretty_select_distinct() {
3495        assert_eq!(
3496            pretty_print("SELECT DISTINCT a, b FROM t"),
3497            "SELECT DISTINCT\n  a,\n  b\nFROM\n  t"
3498        );
3499    }
3500}