sql_cli/sql/parser/
ast_formatter.rs

1//! AST-based SQL Formatter
2//!
3//! This module provides proper SQL formatting by traversing the parsed AST,
4//! which is more reliable than regex-based formatting and handles complex
5//! features like CTEs, subqueries, and expressions correctly.
6
7use crate::sql::parser::ast::*;
8use std::fmt::Write;
9
10/// Configuration for SQL formatting
11pub struct FormatConfig {
12    /// Indentation string (e.g., "  " for 2 spaces, "\t" for tab)
13    pub indent: String,
14    /// Maximum number of items per line for lists (SELECT columns, etc.)
15    pub items_per_line: usize,
16    /// Whether to uppercase keywords
17    pub uppercase_keywords: bool,
18    /// Whether to add newlines between major clauses
19    pub compact: bool,
20}
21
22impl Default for FormatConfig {
23    fn default() -> Self {
24        Self {
25            indent: "    ".to_string(),
26            items_per_line: 5,
27            uppercase_keywords: true,
28            compact: false,
29        }
30    }
31}
32
33/// Format a SELECT statement into pretty SQL
34pub fn format_select_statement(stmt: &SelectStatement) -> String {
35    format_select_with_config(stmt, &FormatConfig::default())
36}
37
38/// Format a SELECT statement with custom configuration
39pub fn format_select_with_config(stmt: &SelectStatement, config: &FormatConfig) -> String {
40    let formatter = AstFormatter::new(config);
41    formatter.format_select(stmt, 0)
42}
43
44struct AstFormatter<'a> {
45    config: &'a FormatConfig,
46}
47
48impl<'a> AstFormatter<'a> {
49    fn new(config: &'a FormatConfig) -> Self {
50        Self { config }
51    }
52
53    fn keyword(&self, word: &str) -> String {
54        if self.config.uppercase_keywords {
55            word.to_uppercase()
56        } else {
57            word.to_lowercase()
58        }
59    }
60
61    fn indent(&self, level: usize) -> String {
62        self.config.indent.repeat(level)
63    }
64
65    fn format_select(&self, stmt: &SelectStatement, indent_level: usize) -> String {
66        let mut result = String::new();
67        let indent = self.indent(indent_level);
68
69        // CTEs (WITH clause)
70        if !stmt.ctes.is_empty() {
71            writeln!(&mut result, "{}{}", indent, self.keyword("WITH")).unwrap();
72            for (i, cte) in stmt.ctes.iter().enumerate() {
73                let is_last = i == stmt.ctes.len() - 1;
74                self.format_cte(&mut result, cte, indent_level + 1, is_last);
75            }
76        }
77
78        // SELECT clause
79        write!(&mut result, "{}{}", indent, self.keyword("SELECT")).unwrap();
80        if stmt.distinct {
81            write!(&mut result, " {}", self.keyword("DISTINCT")).unwrap();
82        }
83
84        // Format select items
85        if stmt.select_items.is_empty() && !stmt.columns.is_empty() {
86            // Legacy columns field
87            self.format_column_list(&mut result, &stmt.columns, indent_level);
88        } else {
89            self.format_select_items(&mut result, &stmt.select_items, indent_level);
90        }
91
92        // FROM clause
93        if let Some(ref table) = stmt.from_table {
94            writeln!(&mut result).unwrap();
95            write!(&mut result, "{}{} {}", indent, self.keyword("FROM"), table).unwrap();
96        } else if let Some(ref subquery) = stmt.from_subquery {
97            writeln!(&mut result).unwrap();
98            write!(&mut result, "{}{} (", indent, self.keyword("FROM")).unwrap();
99            writeln!(&mut result).unwrap();
100            let subquery_sql = self.format_select(subquery, indent_level + 1);
101            write!(&mut result, "{}", subquery_sql).unwrap();
102            write!(&mut result, "\n{}", indent).unwrap();
103            write!(&mut result, ")").unwrap();
104            if let Some(ref alias) = stmt.from_alias {
105                write!(&mut result, " {} {}", self.keyword("AS"), alias).unwrap();
106            }
107        } else if let Some(ref func) = stmt.from_function {
108            writeln!(&mut result).unwrap();
109            write!(&mut result, "{}{} ", indent, self.keyword("FROM")).unwrap();
110            self.format_table_function(&mut result, func);
111            if let Some(ref alias) = stmt.from_alias {
112                write!(&mut result, " {} {}", self.keyword("AS"), alias).unwrap();
113            }
114        }
115
116        // JOIN clauses
117        for join in &stmt.joins {
118            writeln!(&mut result).unwrap();
119            self.format_join(&mut result, join, indent_level);
120        }
121
122        // WHERE clause
123        if let Some(ref where_clause) = stmt.where_clause {
124            writeln!(&mut result).unwrap();
125            write!(&mut result, "{}{}", indent, self.keyword("WHERE")).unwrap();
126            self.format_where_clause(&mut result, where_clause, indent_level);
127        }
128
129        // GROUP BY clause
130        if let Some(ref group_by) = stmt.group_by {
131            writeln!(&mut result).unwrap();
132            write!(&mut result, "{}{} ", indent, self.keyword("GROUP BY")).unwrap();
133            for (i, expr) in group_by.iter().enumerate() {
134                if i > 0 {
135                    write!(&mut result, ", ").unwrap();
136                }
137                write!(&mut result, "{}", self.format_expression(expr)).unwrap();
138            }
139        }
140
141        // HAVING clause
142        if let Some(ref having) = stmt.having {
143            writeln!(&mut result).unwrap();
144            write!(
145                &mut result,
146                "{}{} {}",
147                indent,
148                self.keyword("HAVING"),
149                self.format_expression(having)
150            )
151            .unwrap();
152        }
153
154        // ORDER BY clause
155        if let Some(ref order_by) = stmt.order_by {
156            writeln!(&mut result).unwrap();
157            write!(&mut result, "{}{} ", indent, self.keyword("ORDER BY")).unwrap();
158            for (i, col) in order_by.iter().enumerate() {
159                if i > 0 {
160                    write!(&mut result, ", ").unwrap();
161                }
162                write!(&mut result, "{}", col.column).unwrap();
163                match col.direction {
164                    SortDirection::Asc => write!(&mut result, " {}", self.keyword("ASC")).unwrap(),
165                    SortDirection::Desc => {
166                        write!(&mut result, " {}", self.keyword("DESC")).unwrap()
167                    }
168                }
169            }
170        }
171
172        // LIMIT clause
173        if let Some(limit) = stmt.limit {
174            writeln!(&mut result).unwrap();
175            write!(&mut result, "{}{} {}", indent, self.keyword("LIMIT"), limit).unwrap();
176        }
177
178        // OFFSET clause
179        if let Some(offset) = stmt.offset {
180            writeln!(&mut result).unwrap();
181            write!(
182                &mut result,
183                "{}{} {}",
184                indent,
185                self.keyword("OFFSET"),
186                offset
187            )
188            .unwrap();
189        }
190
191        result
192    }
193
194    fn format_cte(&self, result: &mut String, cte: &CTE, indent_level: usize, is_last: bool) {
195        let indent = self.indent(indent_level);
196
197        // Add WEB keyword for Web CTEs
198        let is_web = matches!(&cte.cte_type, crate::sql::parser::ast::CTEType::Web(_));
199        if is_web {
200            write!(result, "{}{} {}", indent, self.keyword("WEB"), cte.name).unwrap();
201        } else {
202            write!(result, "{}{}", indent, cte.name).unwrap();
203        }
204
205        if let Some(ref columns) = cte.column_list {
206            write!(result, "(").unwrap();
207            for (i, col) in columns.iter().enumerate() {
208                if i > 0 {
209                    write!(result, ", ").unwrap();
210                }
211                write!(result, "{}", col).unwrap();
212            }
213            write!(result, ")").unwrap();
214        }
215
216        writeln!(result, " {} (", self.keyword("AS")).unwrap();
217        let cte_sql = match &cte.cte_type {
218            crate::sql::parser::ast::CTEType::Standard(query) => {
219                self.format_select(query, indent_level + 1)
220            }
221            crate::sql::parser::ast::CTEType::Web(web_spec) => {
222                // Format WEB CTE
223                let mut web_str = format!(
224                    "{}{} '{}'",
225                    "    ".repeat(indent_level + 1),
226                    self.keyword("URL"),
227                    web_spec.url
228                );
229
230                // Add METHOD if specified
231                if let Some(method) = &web_spec.method {
232                    web_str.push_str(&format!(
233                        " {} {}",
234                        self.keyword("METHOD"),
235                        match method {
236                            crate::sql::parser::ast::HttpMethod::GET => "GET",
237                            crate::sql::parser::ast::HttpMethod::POST => "POST",
238                            crate::sql::parser::ast::HttpMethod::PUT => "PUT",
239                            crate::sql::parser::ast::HttpMethod::DELETE => "DELETE",
240                            crate::sql::parser::ast::HttpMethod::PATCH => "PATCH",
241                        }
242                    ));
243                }
244
245                // Add BODY if specified
246                if let Some(body) = &web_spec.body {
247                    web_str.push_str(&format!(" {} '{}'", self.keyword("BODY"), body));
248                }
249
250                // Add FORMAT if specified
251                if let Some(format) = &web_spec.format {
252                    web_str.push_str(&format!(
253                        " {} {}",
254                        self.keyword("FORMAT"),
255                        match format {
256                            crate::sql::parser::ast::DataFormat::CSV => "CSV",
257                            crate::sql::parser::ast::DataFormat::JSON => "JSON",
258                            crate::sql::parser::ast::DataFormat::Auto => "AUTO",
259                        }
260                    ));
261                }
262
263                // Add JSON_PATH if specified
264                if let Some(json_path) = &web_spec.json_path {
265                    web_str.push_str(&format!(" {} '{}'", self.keyword("JSON_PATH"), json_path));
266                }
267
268                // Add CACHE if specified
269                if let Some(cache) = web_spec.cache_seconds {
270                    web_str.push_str(&format!(" {} {}", self.keyword("CACHE"), cache));
271                }
272
273                // Add FORM_FILE entries if specified
274                for (field_name, file_path) in &web_spec.form_files {
275                    web_str.push_str(&format!(
276                        "\n{}{} '{}' '{}'",
277                        "    ".repeat(indent_level + 1),
278                        self.keyword("FORM_FILE"),
279                        field_name,
280                        file_path
281                    ));
282                }
283
284                // Add FORM_FIELD entries if specified
285                for (field_name, value) in &web_spec.form_fields {
286                    // Check if the value looks like JSON (starts with { or [)
287                    let trimmed_value = value.trim();
288                    if (trimmed_value.starts_with('{') && trimmed_value.ends_with('}'))
289                        || (trimmed_value.starts_with('[') && trimmed_value.ends_with(']'))
290                    {
291                        // Try to prettify JSON
292                        match serde_json::from_str::<serde_json::Value>(trimmed_value) {
293                            Ok(json_val) => {
294                                // Pretty print JSON with 2-space indentation
295                                match serde_json::to_string_pretty(&json_val) {
296                                    Ok(pretty_json) => {
297                                        // Add proper indentation for each line
298                                        let base_indent = "    ".repeat(indent_level + 1);
299                                        let json_lines: Vec<String> = pretty_json
300                                            .lines()
301                                            .enumerate()
302                                            .map(|(i, line)| {
303                                                if i == 0 {
304                                                    line.to_string()
305                                                } else {
306                                                    format!("{}{}", base_indent, line)
307                                                }
308                                            })
309                                            .collect();
310                                        let formatted_json = json_lines.join("\n");
311
312                                        web_str.push_str(&format!(
313                                            "\n{}{} '{}' '{}'\n",
314                                            base_indent,
315                                            self.keyword("FORM_FIELD"),
316                                            field_name,
317                                            formatted_json
318                                        ));
319                                    }
320                                    Err(_) => {
321                                        // Fall back to original if pretty print fails
322                                        web_str.push_str(&format!(
323                                            "\n{}{} '{}' '{}'",
324                                            "    ".repeat(indent_level + 1),
325                                            self.keyword("FORM_FIELD"),
326                                            field_name,
327                                            value
328                                        ));
329                                    }
330                                }
331                            }
332                            Err(_) => {
333                                // Not valid JSON, use as-is
334                                web_str.push_str(&format!(
335                                    "\n{}{} '{}' '{}'",
336                                    "    ".repeat(indent_level + 1),
337                                    self.keyword("FORM_FIELD"),
338                                    field_name,
339                                    value
340                                ));
341                            }
342                        }
343                    } else {
344                        // Not JSON, use as-is
345                        web_str.push_str(&format!(
346                            "\n{}{} '{}' '{}'",
347                            "    ".repeat(indent_level + 1),
348                            self.keyword("FORM_FIELD"),
349                            field_name,
350                            value
351                        ));
352                    }
353                }
354
355                // Add HEADERS if specified
356                if !web_spec.headers.is_empty() {
357                    web_str.push_str(&format!(" {} (", self.keyword("HEADERS")));
358                    for (i, (key, value)) in web_spec.headers.iter().enumerate() {
359                        if i > 0 {
360                            web_str.push_str(", ");
361                        }
362                        web_str.push_str(&format!("'{}': '{}'", key, value));
363                    }
364                    web_str.push(')');
365                }
366
367                web_str
368            }
369        };
370        write!(result, "{}", cte_sql).unwrap();
371        writeln!(result).unwrap();
372        write!(result, "{}", indent).unwrap();
373        if is_last {
374            writeln!(result, ")").unwrap();
375        } else {
376            writeln!(result, "),").unwrap();
377        }
378    }
379
380    fn format_column_list(&self, result: &mut String, columns: &[String], indent_level: usize) {
381        if columns.len() <= self.config.items_per_line {
382            // Single line
383            write!(result, " ").unwrap();
384            for (i, col) in columns.iter().enumerate() {
385                if i > 0 {
386                    write!(result, ", ").unwrap();
387                }
388                write!(result, "{}", col).unwrap();
389            }
390        } else {
391            // Multi-line
392            writeln!(result).unwrap();
393            let indent = self.indent(indent_level + 1);
394            for (i, col) in columns.iter().enumerate() {
395                write!(result, "{}{}", indent, col).unwrap();
396                if i < columns.len() - 1 {
397                    writeln!(result, ",").unwrap();
398                }
399            }
400        }
401    }
402
403    fn format_select_items(&self, result: &mut String, items: &[SelectItem], indent_level: usize) {
404        if items.is_empty() {
405            write!(result, " *").unwrap();
406            return;
407        }
408
409        // Count non-star items for formatting decision
410        let non_star_count = items
411            .iter()
412            .filter(|i| !matches!(i, SelectItem::Star))
413            .count();
414
415        // Check if any item is complex (function calls, CASE expressions, etc.)
416        let has_complex_items = items.iter().any(|item| match item {
417            SelectItem::Expression { expr, .. } => self.is_complex_expression(expr),
418            _ => false,
419        });
420
421        // Calculate total approximate length if on single line
422        let single_line_length: usize = items
423            .iter()
424            .map(|item| {
425                match item {
426                    SelectItem::Star => 1,
427                    SelectItem::Column(col) => col.name.len(),
428                    SelectItem::Expression { expr, alias } => {
429                        self.format_expression(expr).len() + 4 + alias.len() // " AS " = 4
430                    }
431                }
432            })
433            .sum::<usize>()
434            + (items.len() - 1) * 2; // ", " between items
435
436        // Use multi-line formatting by default unless:
437        // - It's a single simple column or star
438        // - It's 2-3 simple columns with total length < 40 chars
439        let use_single_line = match items.len() {
440            1 => !has_complex_items, // Single item: only if simple
441            2..=3 => !has_complex_items && single_line_length < 40, // 2-3 items: only if very short
442            _ => false,              // 4+ items: always multi-line
443        };
444
445        if !use_single_line {
446            // Multi-line
447            writeln!(result).unwrap();
448            let indent = self.indent(indent_level + 1);
449            for (i, item) in items.iter().enumerate() {
450                write!(result, "{}", indent).unwrap();
451                self.format_select_item(result, item);
452                if i < items.len() - 1 {
453                    writeln!(result, ",").unwrap();
454                }
455            }
456        } else {
457            // Single line
458            write!(result, " ").unwrap();
459            for (i, item) in items.iter().enumerate() {
460                if i > 0 {
461                    write!(result, ", ").unwrap();
462                }
463                self.format_select_item(result, item);
464            }
465        }
466    }
467
468    fn is_complex_expression(&self, expr: &SqlExpression) -> bool {
469        match expr {
470            SqlExpression::CaseExpression { .. } => true,
471            SqlExpression::FunctionCall { .. } => true,
472            SqlExpression::WindowFunction { .. } => true,
473            SqlExpression::ScalarSubquery { .. } => true,
474            SqlExpression::InSubquery { .. } => true,
475            SqlExpression::NotInSubquery { .. } => true,
476            SqlExpression::BinaryOp { left, right, .. } => {
477                self.is_complex_expression(left) || self.is_complex_expression(right)
478            }
479            _ => false,
480        }
481    }
482
483    fn format_select_item(&self, result: &mut String, item: &SelectItem) {
484        match item {
485            SelectItem::Star => write!(result, "*").unwrap(),
486            SelectItem::Column(col) => write!(result, "{}", col.to_sql()).unwrap(),
487            SelectItem::Expression { expr, alias } => {
488                write!(
489                    result,
490                    "{} {} {}",
491                    self.format_expression(expr),
492                    self.keyword("AS"),
493                    alias
494                )
495                .unwrap();
496            }
497        }
498    }
499
500    fn format_expression(&self, expr: &SqlExpression) -> String {
501        match expr {
502            SqlExpression::Column(column_ref) => column_ref.to_sql(),
503            SqlExpression::StringLiteral(s) => format!("'{}'", s),
504            SqlExpression::NumberLiteral(n) => n.clone(),
505            SqlExpression::BooleanLiteral(b) => b.to_string().to_uppercase(),
506            SqlExpression::Null => self.keyword("NULL"),
507            SqlExpression::BinaryOp { left, op, right } => {
508                // Special handling for IS NULL / IS NOT NULL operators
509                if op == "IS NULL" || op == "IS NOT NULL" {
510                    format!("{} {}", self.format_expression(left), op)
511                } else {
512                    format!(
513                        "{} {} {}",
514                        self.format_expression(left),
515                        op,
516                        self.format_expression(right)
517                    )
518                }
519            }
520            SqlExpression::FunctionCall {
521                name,
522                args,
523                distinct,
524            } => {
525                let mut result = name.clone();
526                result.push('(');
527                if *distinct {
528                    result.push_str(&self.keyword("DISTINCT"));
529                    result.push(' ');
530                }
531                for (i, arg) in args.iter().enumerate() {
532                    if i > 0 {
533                        result.push_str(", ");
534                    }
535                    result.push_str(&self.format_expression(arg));
536                }
537                result.push(')');
538                result
539            }
540            SqlExpression::CaseExpression {
541                when_branches,
542                else_branch,
543            } => {
544                // Format CASE expressions on multiple lines for readability
545                let mut result = String::new();
546                result.push_str(&self.keyword("CASE"));
547                result.push('\n');
548
549                // Format each WHEN branch on its own line with indentation
550                for branch in when_branches {
551                    result.push_str("        "); // 8 spaces for WHEN indent
552                    result.push_str(&format!(
553                        "{} {} {} {}",
554                        self.keyword("WHEN"),
555                        self.format_expression(&branch.condition),
556                        self.keyword("THEN"),
557                        self.format_expression(&branch.result)
558                    ));
559                    result.push('\n');
560                }
561
562                // Format ELSE clause if present
563                if let Some(else_expr) = else_branch {
564                    result.push_str("        "); // 8 spaces for ELSE indent
565                    result.push_str(&format!(
566                        "{} {}",
567                        self.keyword("ELSE"),
568                        self.format_expression(else_expr)
569                    ));
570                    result.push('\n');
571                }
572
573                result.push_str("    "); // 4 spaces for END
574                result.push_str(&self.keyword("END"));
575                result
576            }
577            SqlExpression::Between { expr, lower, upper } => {
578                format!(
579                    "{} {} {} {} {}",
580                    self.format_expression(expr),
581                    self.keyword("BETWEEN"),
582                    self.format_expression(lower),
583                    self.keyword("AND"),
584                    self.format_expression(upper)
585                )
586            }
587            SqlExpression::InList { expr, values } => {
588                let mut result =
589                    format!("{} {} (", self.format_expression(expr), self.keyword("IN"));
590                for (i, val) in values.iter().enumerate() {
591                    if i > 0 {
592                        result.push_str(", ");
593                    }
594                    result.push_str(&self.format_expression(val));
595                }
596                result.push(')');
597                result
598            }
599            SqlExpression::NotInList { expr, values } => {
600                let mut result = format!(
601                    "{} {} {} (",
602                    self.format_expression(expr),
603                    self.keyword("NOT"),
604                    self.keyword("IN")
605                );
606                for (i, val) in values.iter().enumerate() {
607                    if i > 0 {
608                        result.push_str(", ");
609                    }
610                    result.push_str(&self.format_expression(val));
611                }
612                result.push(')');
613                result
614            }
615            SqlExpression::Not { expr } => {
616                format!("{} {}", self.keyword("NOT"), self.format_expression(expr))
617            }
618            SqlExpression::ScalarSubquery { query } => {
619                // Check if subquery is complex enough to warrant multi-line formatting
620                let subquery_str = self.format_select(query, 0);
621                if subquery_str.contains('\n') || subquery_str.len() > 60 {
622                    // Multi-line formatting
623                    format!("(\n{}\n)", self.format_select(query, 1))
624                } else {
625                    // Inline formatting
626                    format!("({})", subquery_str)
627                }
628            }
629            SqlExpression::InSubquery { expr, subquery } => {
630                let subquery_str = self.format_select(subquery, 0);
631                if subquery_str.contains('\n') || subquery_str.len() > 60 {
632                    // Multi-line formatting
633                    format!(
634                        "{} {} (\n{}\n)",
635                        self.format_expression(expr),
636                        self.keyword("IN"),
637                        self.format_select(subquery, 1)
638                    )
639                } else {
640                    // Inline formatting
641                    format!(
642                        "{} {} ({})",
643                        self.format_expression(expr),
644                        self.keyword("IN"),
645                        subquery_str
646                    )
647                }
648            }
649            SqlExpression::NotInSubquery { expr, subquery } => {
650                let subquery_str = self.format_select(subquery, 0);
651                if subquery_str.contains('\n') || subquery_str.len() > 60 {
652                    // Multi-line formatting
653                    format!(
654                        "{} {} {} (\n{}\n)",
655                        self.format_expression(expr),
656                        self.keyword("NOT"),
657                        self.keyword("IN"),
658                        self.format_select(subquery, 1)
659                    )
660                } else {
661                    // Inline formatting
662                    format!(
663                        "{} {} {} ({})",
664                        self.format_expression(expr),
665                        self.keyword("NOT"),
666                        self.keyword("IN"),
667                        subquery_str
668                    )
669                }
670            }
671            SqlExpression::MethodCall {
672                object,
673                method,
674                args,
675            } => {
676                let mut result = format!("{}.{}", object, method);
677                result.push('(');
678                for (i, arg) in args.iter().enumerate() {
679                    if i > 0 {
680                        result.push_str(", ");
681                    }
682                    result.push_str(&self.format_expression(arg));
683                }
684                result.push(')');
685                result
686            }
687            SqlExpression::ChainedMethodCall { base, method, args } => {
688                let mut result = format!("{}.{}", self.format_expression(base), method);
689                result.push('(');
690                for (i, arg) in args.iter().enumerate() {
691                    if i > 0 {
692                        result.push_str(", ");
693                    }
694                    result.push_str(&self.format_expression(arg));
695                }
696                result.push(')');
697                result
698            }
699            SqlExpression::WindowFunction {
700                name,
701                args,
702                window_spec,
703            } => {
704                let mut result = format!("{}(", name);
705
706                // Add function arguments
707                for (i, arg) in args.iter().enumerate() {
708                    if i > 0 {
709                        result.push_str(", ");
710                    }
711                    result.push_str(&self.format_expression(arg));
712                }
713                result.push_str(") ");
714                result.push_str(&self.keyword("OVER"));
715                result.push_str(" (");
716
717                // Add PARTITION BY clause if present
718                if !window_spec.partition_by.is_empty() {
719                    result.push_str(&self.keyword("PARTITION BY"));
720                    result.push(' ');
721                    for (i, col) in window_spec.partition_by.iter().enumerate() {
722                        if i > 0 {
723                            result.push_str(", ");
724                        }
725                        result.push_str(col);
726                    }
727                }
728
729                // Add ORDER BY clause if present
730                if !window_spec.order_by.is_empty() {
731                    if !window_spec.partition_by.is_empty() {
732                        result.push(' ');
733                    }
734                    result.push_str(&self.keyword("ORDER BY"));
735                    result.push(' ');
736                    for (i, col) in window_spec.order_by.iter().enumerate() {
737                        if i > 0 {
738                            result.push_str(", ");
739                        }
740                        result.push_str(&col.column);
741                        match col.direction {
742                            SortDirection::Asc => {
743                                result.push(' ');
744                                result.push_str(&self.keyword("ASC"));
745                            }
746                            SortDirection::Desc => {
747                                result.push(' ');
748                                result.push_str(&self.keyword("DESC"));
749                            }
750                        }
751                    }
752                }
753
754                result.push(')');
755                result
756            }
757            _ => format!("{:?}", expr), // Fallback for unhandled expression types
758        }
759    }
760
761    fn format_where_clause(
762        &self,
763        result: &mut String,
764        where_clause: &WhereClause,
765        indent_level: usize,
766    ) {
767        let needs_multiline = where_clause.conditions.len() > 1;
768
769        if needs_multiline {
770            writeln!(result).unwrap();
771            let indent = self.indent(indent_level + 1);
772            for (i, condition) in where_clause.conditions.iter().enumerate() {
773                if i > 0 {
774                    if let Some(ref connector) = where_clause.conditions[i - 1].connector {
775                        let connector_str = match connector {
776                            LogicalOp::And => self.keyword("AND"),
777                            LogicalOp::Or => self.keyword("OR"),
778                        };
779                        writeln!(result).unwrap();
780                        write!(result, "{}{} ", indent, connector_str).unwrap();
781                    }
782                } else {
783                    write!(result, "{}", indent).unwrap();
784                }
785                write!(result, "{}", self.format_expression(&condition.expr)).unwrap();
786            }
787        } else if let Some(condition) = where_clause.conditions.first() {
788            write!(result, " {}", self.format_expression(&condition.expr)).unwrap();
789        }
790    }
791
792    fn format_join(&self, result: &mut String, join: &JoinClause, indent_level: usize) {
793        let indent = self.indent(indent_level);
794        let join_type = match join.join_type {
795            JoinType::Inner => self.keyword("INNER JOIN"),
796            JoinType::Left => self.keyword("LEFT JOIN"),
797            JoinType::Right => self.keyword("RIGHT JOIN"),
798            JoinType::Full => self.keyword("FULL JOIN"),
799            JoinType::Cross => self.keyword("CROSS JOIN"),
800        };
801
802        write!(result, "{}{} ", indent, join_type).unwrap();
803
804        match &join.table {
805            TableSource::Table(name) => write!(result, "{}", name).unwrap(),
806            TableSource::DerivedTable { query, alias } => {
807                writeln!(result, "(").unwrap();
808                let subquery_sql = self.format_select(query, indent_level + 1);
809                write!(result, "{}", subquery_sql).unwrap();
810                writeln!(result).unwrap();
811                write!(result, "{}) {} {}", indent, self.keyword("AS"), alias).unwrap();
812            }
813        }
814
815        if let Some(ref alias) = join.alias {
816            write!(result, " {} {}", self.keyword("AS"), alias).unwrap();
817        }
818
819        if !join.condition.conditions.is_empty() {
820            write!(result, " {}", self.keyword("ON")).unwrap();
821            for (i, condition) in join.condition.conditions.iter().enumerate() {
822                if i > 0 {
823                    write!(result, " {}", self.keyword("AND")).unwrap();
824                }
825                write!(
826                    result,
827                    " {} {} {}",
828                    condition.left_column,
829                    self.format_join_operator(&condition.operator),
830                    condition.right_column
831                )
832                .unwrap();
833            }
834        }
835    }
836
837    fn format_join_operator(&self, op: &JoinOperator) -> String {
838        match op {
839            JoinOperator::Equal => "=",
840            JoinOperator::NotEqual => "!=",
841            JoinOperator::LessThan => "<",
842            JoinOperator::GreaterThan => ">",
843            JoinOperator::LessThanOrEqual => "<=",
844            JoinOperator::GreaterThanOrEqual => ">=",
845        }
846        .to_string()
847    }
848
849    fn format_table_function(&self, result: &mut String, func: &TableFunction) {
850        match func {
851            TableFunction::Generator { name, args } => {
852                write!(result, "{}(", self.keyword(&name.to_uppercase())).unwrap();
853                for (i, arg) in args.iter().enumerate() {
854                    if i > 0 {
855                        write!(result, ", ").unwrap();
856                    }
857                    write!(result, "{}", self.format_expression(arg)).unwrap();
858                }
859                write!(result, ")").unwrap();
860            }
861        }
862    }
863}
864
865/// Parse and format SQL query using the AST
866pub fn format_sql_ast(query: &str) -> Result<String, String> {
867    use crate::sql::recursive_parser::Parser;
868
869    let mut parser = Parser::new(query);
870    match parser.parse() {
871        Ok(stmt) => Ok(format_select_statement(&stmt)),
872        Err(e) => Err(format!("Parse error: {}", e)),
873    }
874}
875
876/// Parse and format SQL with custom configuration
877pub fn format_sql_ast_with_config(query: &str, config: &FormatConfig) -> Result<String, String> {
878    use crate::sql::recursive_parser::Parser;
879
880    let mut parser = Parser::new(query);
881    match parser.parse() {
882        Ok(stmt) => Ok(format_select_with_config(&stmt, &config)),
883        Err(e) => Err(format!("Parse error: {}", e)),
884    }
885}