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