Skip to main content

polyglot_sql/
generator.rs

1//! SQL Generator -- converts an AST back into SQL strings.
2//!
3//! The central type is [`Generator`], which walks an [`Expression`] tree and
4//! emits a SQL string. Generation is controlled by a [`GeneratorConfig`] that
5//! specifies the target dialect, formatting preferences, identifier quoting
6//! style, function name casing, and many other dialect-specific flags.
7//!
8//! For one-off generation the static helpers [`Generator::sql`] and
9//! [`Generator::pretty_sql`] are the simplest entry points. For repeated
10//! generation, construct a `Generator` once with [`Generator::with_config`]
11//! and call [`Generator::generate`] for each expression.
12
13use crate::error::Result;
14use crate::expressions::*;
15use crate::DialectType;
16
17/// SQL code generator that converts an AST (`Expression`) back into a SQL string.
18///
19/// The generator walks the expression tree and emits dialect-specific SQL text.
20/// It supports pretty-printing with configurable indentation, identifier quoting,
21/// keyword casing, function name normalization, and 30+ SQL dialect variants.
22///
23/// # Usage
24///
25/// ```rust,ignore
26/// use polyglot_sql::generator::Generator;
27/// use polyglot_sql::parser::Parser;
28///
29/// let ast = Parser::parse_sql("SELECT 1")?;
30/// // Quick one-shot generation (default config):
31/// let sql = Generator::sql(&ast[0])?;
32///
33/// // Pretty-printed output:
34/// let pretty = Generator::pretty_sql(&ast[0])?;
35///
36/// // Custom config (e.g. for a specific dialect):
37/// let config = GeneratorConfig { pretty: true, ..GeneratorConfig::default() };
38/// let mut gen = Generator::with_config(config);
39/// let sql = gen.generate(&ast[0])?;
40/// ```
41pub struct Generator {
42    config: GeneratorConfig,
43    output: String,
44    indent_level: usize,
45    /// Athena dialect: true when generating Hive-style DDL (uses backticks)
46    /// false when generating Trino-style DML/CREATE VIEW (uses double quotes)
47    athena_hive_context: bool,
48    /// SQLite: column names that should have PRIMARY KEY inlined (from single-column table constraints)
49    sqlite_inline_pk_columns: std::collections::HashSet<String>,
50    /// MERGE: table name/alias qualifiers to strip from UPDATE SET left side (for PostgreSQL)
51    merge_strip_qualifiers: Vec<String>,
52}
53
54/// Controls how SQL function names are cased in generated output.
55///
56/// - `Upper` (default) -- `COUNT`, `SUM`, `COALESCE`
57/// - `Lower` -- `count`, `sum`, `coalesce`
58/// - `None` -- preserve the original casing from the parsed input
59#[derive(Debug, Clone, Copy, PartialEq, Default)]
60pub enum NormalizeFunctions {
61    /// Emit function names in UPPER CASE (default).
62    #[default]
63    Upper,
64    /// Emit function names in lower case.
65    Lower,
66    /// Preserve the original casing from the parsed input.
67    None,
68}
69
70/// Strategy for generating row-limiting clauses across SQL dialects.
71#[derive(Debug, Clone, Copy, PartialEq, Default)]
72pub enum LimitFetchStyle {
73    /// `LIMIT n` -- MySQL, PostgreSQL, DuckDB, and most modern dialects.
74    #[default]
75    Limit,
76    /// `TOP n` -- TSQL (SQL Server).
77    Top,
78    /// `FETCH FIRST n ROWS ONLY` -- ISO/ANSI SQL standard, Oracle, DB2.
79    FetchFirst,
80}
81
82/// Identifier quote style (start/end characters)
83#[derive(Debug, Clone, Copy, PartialEq)]
84pub struct IdentifierQuoteStyle {
85    /// Start character for quoting identifiers (e.g., '"', '`', '[')
86    pub start: char,
87    /// End character for quoting identifiers (e.g., '"', '`', ']')
88    pub end: char,
89}
90
91impl Default for IdentifierQuoteStyle {
92    fn default() -> Self {
93        Self { start: '"', end: '"' }
94    }
95}
96
97impl IdentifierQuoteStyle {
98    /// Double-quote style (PostgreSQL, Oracle, standard SQL)
99    pub const DOUBLE_QUOTE: Self = Self { start: '"', end: '"' };
100    /// Backtick style (MySQL, BigQuery, Spark, Hive)
101    pub const BACKTICK: Self = Self { start: '`', end: '`' };
102    /// Square bracket style (TSQL, SQLite)
103    pub const BRACKET: Self = Self { start: '[', end: ']' };
104}
105
106/// Configuration for the SQL [`Generator`].
107///
108/// This is a comprehensive port of the Python sqlglot `Generator` class attributes.
109/// It controls every aspect of SQL output: formatting, quoting, dialect-specific
110/// syntax, feature support flags, and more.
111///
112/// Most users should start from `GeneratorConfig::default()` and override only the
113/// fields they need. Dialect-specific presets are applied automatically when
114/// `dialect` is set via the higher-level transpilation API.
115///
116/// # Key fields
117///
118/// | Field | Default | Purpose |
119/// |-------|---------|---------|
120/// | `dialect` | `None` | Target SQL dialect (e.g. PostgreSQL, MySQL, BigQuery) |
121/// | `pretty` | `false` | Enable multi-line, indented output |
122/// | `indent` | `"  "` | Indentation string used when `pretty` is true |
123/// | `max_text_width` | `80` | Soft line-width limit for pretty-printing |
124/// | `normalize_functions` | `Upper` | Function name casing (`Upper`, `Lower`, `None`) |
125/// | `identifier_quote_style` | `"…"` | Quote characters for identifiers |
126/// | `uppercase_keywords` | `true` | Whether SQL keywords are upper-cased |
127#[derive(Debug, Clone)]
128pub struct GeneratorConfig {
129    // ===== Basic formatting =====
130    /// Pretty print with indentation
131    pub pretty: bool,
132    /// Indentation string (default 2 spaces)
133    pub indent: String,
134    /// Maximum text width before wrapping (default 80)
135    pub max_text_width: usize,
136    /// Quote identifier style (deprecated, use identifier_quote_style instead)
137    pub identifier_quote: char,
138    /// Identifier quote style with separate start/end characters
139    pub identifier_quote_style: IdentifierQuoteStyle,
140    /// Uppercase keywords
141    pub uppercase_keywords: bool,
142    /// Normalize identifiers to lowercase when generating
143    pub normalize_identifiers: bool,
144    /// Dialect type for dialect-specific generation
145    pub dialect: Option<crate::dialects::DialectType>,
146    /// How to output function names (UPPER, lower, or as-is)
147    pub normalize_functions: NormalizeFunctions,
148    /// String escape character
149    pub string_escape: char,
150    /// Whether identifiers are case-sensitive
151    pub case_sensitive_identifiers: bool,
152    /// Whether unquoted identifiers can start with a digit
153    pub identifiers_can_start_with_digit: bool,
154    /// Whether to always quote identifiers regardless of reserved keyword status
155    /// Used by dialects like Athena/Presto that prefer quoted identifiers
156    pub always_quote_identifiers: bool,
157
158    // ===== Null handling =====
159    /// Whether null ordering (NULLS FIRST/LAST) is supported in ORDER BY
160    /// True: Full Support, false: No support
161    pub null_ordering_supported: bool,
162    /// Whether ignore nulls is inside the agg or outside
163    /// FIRST(x IGNORE NULLS) OVER vs FIRST(x) IGNORE NULLS OVER
164    pub ignore_nulls_in_func: bool,
165    /// Whether the NVL2 function is supported
166    pub nvl2_supported: bool,
167
168    // ===== Limit/Fetch =====
169    /// How to output LIMIT clauses
170    pub limit_fetch_style: LimitFetchStyle,
171    /// Whether to generate the limit as TOP <value> instead of LIMIT <value>
172    pub limit_is_top: bool,
173    /// Whether limit and fetch allows expressions or just literals
174    pub limit_only_literals: bool,
175
176    // ===== Interval =====
177    /// Whether INTERVAL uses single quoted string ('1 day' vs 1 DAY)
178    pub single_string_interval: bool,
179    /// Whether the plural form of date parts (e.g., "days") is supported in INTERVALs
180    pub interval_allows_plural_form: bool,
181
182    // ===== CTE =====
183    /// Whether WITH RECURSIVE keyword is required (vs just WITH for recursive CTEs)
184    pub cte_recursive_keyword_required: bool,
185
186    // ===== VALUES =====
187    /// Whether VALUES can be used as a table source
188    pub values_as_table: bool,
189    /// Wrap derived values in parens (standard but Spark doesn't support)
190    pub wrap_derived_values: bool,
191
192    // ===== TABLESAMPLE =====
193    /// Keyword for TABLESAMPLE seed: "SEED" or "REPEATABLE"
194    pub tablesample_seed_keyword: &'static str,
195    /// Whether parentheses are required around the table sample's expression
196    pub tablesample_requires_parens: bool,
197    /// Whether a table sample clause's size needs to be followed by ROWS keyword
198    pub tablesample_size_is_rows: bool,
199    /// The keyword(s) to use when generating a sample clause
200    pub tablesample_keywords: &'static str,
201    /// Whether the TABLESAMPLE clause supports a method name, like BERNOULLI
202    pub tablesample_with_method: bool,
203    /// Whether the table alias comes after tablesample (Oracle, Hive)
204    pub alias_post_tablesample: bool,
205
206    // ===== Aggregate =====
207    /// Whether aggregate FILTER (WHERE ...) is supported
208    pub aggregate_filter_supported: bool,
209    /// Whether DISTINCT can be followed by multiple args in an AggFunc
210    pub multi_arg_distinct: bool,
211    /// Whether ANY/ALL quantifiers have no space before `(`: `ANY(` vs `ANY (`
212    pub quantified_no_paren_space: bool,
213    /// Whether MEDIAN(expr) is supported; if not, generates PERCENTILE_CONT
214    pub supports_median: bool,
215
216    // ===== SELECT =====
217    /// Whether SELECT ... INTO is supported
218    pub supports_select_into: bool,
219    /// Whether locking reads (SELECT ... FOR UPDATE/SHARE) are supported
220    pub locking_reads_supported: bool,
221
222    // ===== Table/Join =====
223    /// Whether a table is allowed to be renamed with a db
224    pub rename_table_with_db: bool,
225    /// Whether JOIN sides (LEFT, RIGHT) are supported with SEMI/ANTI join kinds
226    pub semi_anti_join_with_side: bool,
227    /// Whether named columns are allowed in table aliases
228    pub supports_table_alias_columns: bool,
229    /// Whether join hints should be generated
230    pub join_hints: bool,
231    /// Whether table hints should be generated
232    pub table_hints: bool,
233    /// Whether query hints should be generated
234    pub query_hints: bool,
235    /// What kind of separator to use for query hints
236    pub query_hint_sep: &'static str,
237    /// Whether Oracle-style (+) join markers are supported (Oracle, Exasol)
238    pub supports_column_join_marks: bool,
239
240    // ===== DDL =====
241    /// Whether CREATE INDEX USING method should have no space before column parens
242    /// true: `USING btree(col)`, false: `USING btree (col)`
243    pub index_using_no_space: bool,
244    /// Whether UNLOGGED tables can be created
245    pub supports_unlogged_tables: bool,
246    /// Whether CREATE TABLE LIKE statement is supported
247    pub supports_create_table_like: bool,
248    /// Whether the LikeProperty needs to be inside the schema clause
249    pub like_property_inside_schema: bool,
250    /// Whether the word COLUMN is included when adding a column with ALTER TABLE
251    pub alter_table_include_column_keyword: bool,
252    /// Whether CREATE TABLE .. COPY .. is supported (false = CLONE instead)
253    pub supports_table_copy: bool,
254    /// The syntax to use when altering the type of a column
255    pub alter_set_type: &'static str,
256    /// Whether to wrap <props> in AlterSet, e.g., ALTER ... SET (<props>)
257    pub alter_set_wrapped: bool,
258
259    // ===== Timestamp/Timezone =====
260    /// Whether TIMESTAMP WITH TIME ZONE is used (vs TIMESTAMPTZ)
261    pub tz_to_with_time_zone: bool,
262    /// Whether CONVERT_TIMEZONE() is supported
263    pub supports_convert_timezone: bool,
264
265    // ===== JSON =====
266    /// Whether the JSON extraction operators expect a value of type JSON
267    pub json_type_required_for_extraction: bool,
268    /// Whether bracketed keys like ["foo"] are supported in JSON paths
269    pub json_path_bracketed_key_supported: bool,
270    /// Whether to escape keys using single quotes in JSON paths
271    pub json_path_single_quote_escape: bool,
272    /// Whether to quote the generated expression of JsonPath
273    pub quote_json_path: bool,
274    /// What delimiter to use for separating JSON key/value pairs
275    pub json_key_value_pair_sep: &'static str,
276
277    // ===== COPY =====
278    /// Whether parameters from COPY statement are wrapped in parentheses
279    pub copy_params_are_wrapped: bool,
280    /// Whether values of params are set with "=" token or empty space
281    pub copy_params_eq_required: bool,
282    /// Whether COPY statement has INTO keyword
283    pub copy_has_into_keyword: bool,
284
285    // ===== Window functions =====
286    /// Whether EXCLUDE in window specification is supported
287    pub supports_window_exclude: bool,
288    /// UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
289    pub unnest_with_ordinality: bool,
290    /// Whether window frame keywords (ROWS/RANGE/GROUPS, PRECEDING/FOLLOWING) should be lowercase
291    /// Exasol uses lowercase for these specific keywords
292    pub lowercase_window_frame_keywords: bool,
293    /// Whether to normalize single-bound window frames to BETWEEN form
294    /// e.g., ROWS 1 PRECEDING → ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
295    pub normalize_window_frame_between: bool,
296
297    // ===== Array =====
298    /// Whether ARRAY_CONCAT can be generated with varlen args
299    pub array_concat_is_var_len: bool,
300    /// Whether exp.ArraySize should generate the dimension arg too
301    /// None -> Doesn't support, false -> optional, true -> required
302    pub array_size_dim_required: Option<bool>,
303    /// Whether any(f(x) for x in array) can be implemented
304    pub can_implement_array_any: bool,
305    /// Function used for array size
306    pub array_size_name: &'static str,
307
308    // ===== BETWEEN =====
309    /// Whether SYMMETRIC and ASYMMETRIC flags are supported with BETWEEN
310    pub supports_between_flags: bool,
311
312    // ===== Boolean =====
313    /// Whether comparing against booleans (e.g. x IS TRUE) is supported
314    pub is_bool_allowed: bool,
315    /// Whether conditions require booleans WHERE x = 0 vs WHERE x
316    pub ensure_bools: bool,
317
318    // ===== EXTRACT =====
319    /// Whether to generate an unquoted value for EXTRACT's date part argument
320    pub extract_allows_quotes: bool,
321    /// Whether to normalize date parts in EXTRACT
322    pub normalize_extract_date_parts: bool,
323
324    // ===== Other features =====
325    /// Whether the conditional TRY(expression) function is supported
326    pub try_supported: bool,
327    /// Whether the UESCAPE syntax in unicode strings is supported
328    pub supports_uescape: bool,
329    /// Whether the function TO_NUMBER is supported
330    pub supports_to_number: bool,
331    /// Whether CONCAT requires >1 arguments
332    pub supports_single_arg_concat: bool,
333    /// Whether LAST_DAY function supports a date part argument
334    pub last_day_supports_date_part: bool,
335    /// Whether a projection can explode into multiple rows
336    pub supports_exploding_projections: bool,
337    /// Whether UNIX_SECONDS(timestamp) is supported
338    pub supports_unix_seconds: bool,
339    /// Whether LIKE and ILIKE support quantifiers such as LIKE ANY/ALL/SOME
340    pub supports_like_quantifiers: bool,
341    /// Whether multi-argument DECODE(...) function is supported
342    pub supports_decode_case: bool,
343    /// Whether set op modifiers apply to the outer set op or select
344    pub set_op_modifiers: bool,
345    /// Whether FROM is supported in UPDATE statements
346    pub update_statement_supports_from: bool,
347
348    // ===== COLLATE =====
349    /// Whether COLLATE is a function instead of a binary operator
350    pub collate_is_func: bool,
351
352    // ===== INSERT =====
353    /// Whether to include "SET" keyword in "INSERT ... ON DUPLICATE KEY UPDATE"
354    pub duplicate_key_update_with_set: bool,
355    /// INSERT OVERWRITE TABLE x override
356    pub insert_overwrite: &'static str,
357
358    // ===== RETURNING =====
359    /// Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ...
360    pub returning_end: bool,
361
362    // ===== MERGE =====
363    /// Whether MERGE ... WHEN MATCHED BY SOURCE is allowed
364    pub matched_by_source: bool,
365
366    // ===== CREATE FUNCTION =====
367    /// Whether create function uses an AS before the RETURN
368    pub create_function_return_as: bool,
369    /// Whether to use = instead of DEFAULT for parameter defaults (TSQL style)
370    pub parameter_default_equals: bool,
371
372    // ===== COMPUTED COLUMN =====
373    /// Whether to include the type of a computed column in the CREATE DDL
374    pub computed_column_with_type: bool,
375
376    // ===== UNPIVOT =====
377    /// Whether UNPIVOT aliases are Identifiers (false means they're Literals)
378    pub unpivot_aliases_are_identifiers: bool,
379
380    // ===== STAR =====
381    /// The keyword to use when generating a star projection with excluded columns
382    pub star_except: &'static str,
383
384    // ===== HEX =====
385    /// The HEX function name
386    pub hex_func: &'static str,
387
388    // ===== WITH =====
389    /// The keywords to use when prefixing WITH based properties
390    pub with_properties_prefix: &'static str,
391
392    // ===== PAD =====
393    /// Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional
394    pub pad_fill_pattern_is_required: bool,
395
396    // ===== INDEX =====
397    /// The string used for creating an index on a table
398    pub index_on: &'static str,
399
400    // ===== GROUPING =====
401    /// The separator for grouping sets and rollups
402    pub groupings_sep: &'static str,
403
404    // ===== STRUCT =====
405    /// Delimiters for STRUCT type
406    pub struct_delimiter: (&'static str, &'static str),
407    /// Whether Struct expressions use curly brace notation: {'key': value} (DuckDB)
408    pub struct_curly_brace_notation: bool,
409    /// Whether Array expressions omit the ARRAY keyword: [1, 2] instead of ARRAY[1, 2]
410    pub array_bracket_only: bool,
411    /// Separator between struct field name and type (": " for Hive, " " for others)
412    pub struct_field_sep: &'static str,
413
414    // ===== EXCEPT/INTERSECT =====
415    /// Whether EXCEPT and INTERSECT operations can return duplicates
416    pub except_intersect_support_all_clause: bool,
417
418    // ===== PARAMETERS/PLACEHOLDERS =====
419    /// Parameter token character (@ for TSQL, $ for PostgreSQL)
420    pub parameter_token: &'static str,
421    /// Named placeholder token (: for most, % for PostgreSQL)
422    pub named_placeholder_token: &'static str,
423
424    // ===== DATA TYPES =====
425    /// Whether data types support additional specifiers like CHAR or BYTE (oracle)
426    pub data_type_specifiers_allowed: bool,
427
428    // ===== COMMENT =====
429    /// Whether schema comments use `=` sign (COMMENT='value' vs COMMENT 'value')
430    /// StarRocks and Doris use naked COMMENT syntax without `=`
431    pub schema_comment_with_eq: bool,
432}
433
434impl Default for GeneratorConfig {
435    fn default() -> Self {
436        Self {
437            // ===== Basic formatting =====
438            pretty: false,
439            indent: "  ".to_string(),
440            max_text_width: 80,
441            identifier_quote: '"',
442            identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
443            uppercase_keywords: true,
444            normalize_identifiers: false,
445            dialect: None,
446            normalize_functions: NormalizeFunctions::Upper,
447            string_escape: '\'',
448            case_sensitive_identifiers: false,
449            identifiers_can_start_with_digit: false,
450            always_quote_identifiers: false,
451
452            // ===== Null handling =====
453            null_ordering_supported: true,
454            ignore_nulls_in_func: false,
455            nvl2_supported: true,
456
457            // ===== Limit/Fetch =====
458            limit_fetch_style: LimitFetchStyle::Limit,
459            limit_is_top: false,
460            limit_only_literals: false,
461
462            // ===== Interval =====
463            single_string_interval: false,
464            interval_allows_plural_form: true,
465
466            // ===== CTE =====
467            cte_recursive_keyword_required: true,
468
469            // ===== VALUES =====
470            values_as_table: true,
471            wrap_derived_values: true,
472
473            // ===== TABLESAMPLE =====
474            tablesample_seed_keyword: "SEED",
475            tablesample_requires_parens: true,
476            tablesample_size_is_rows: true,
477            tablesample_keywords: "TABLESAMPLE",
478            tablesample_with_method: true,
479            alias_post_tablesample: false,
480
481            // ===== Aggregate =====
482            aggregate_filter_supported: true,
483            multi_arg_distinct: true,
484            quantified_no_paren_space: false,
485            supports_median: true,
486
487            // ===== SELECT =====
488            supports_select_into: false,
489            locking_reads_supported: true,
490
491            // ===== Table/Join =====
492            rename_table_with_db: true,
493            semi_anti_join_with_side: true,
494            supports_table_alias_columns: true,
495            join_hints: true,
496            table_hints: true,
497            query_hints: true,
498            query_hint_sep: ", ",
499            supports_column_join_marks: false,
500
501            // ===== DDL =====
502            index_using_no_space: false,
503            supports_unlogged_tables: false,
504            supports_create_table_like: true,
505            like_property_inside_schema: false,
506            alter_table_include_column_keyword: true,
507            supports_table_copy: true,
508            alter_set_type: "SET DATA TYPE",
509            alter_set_wrapped: false,
510
511            // ===== Timestamp/Timezone =====
512            tz_to_with_time_zone: false,
513            supports_convert_timezone: false,
514
515            // ===== JSON =====
516            json_type_required_for_extraction: false,
517            json_path_bracketed_key_supported: true,
518            json_path_single_quote_escape: false,
519            quote_json_path: true,
520            json_key_value_pair_sep: ":",
521
522            // ===== COPY =====
523            copy_params_are_wrapped: true,
524            copy_params_eq_required: false,
525            copy_has_into_keyword: true,
526
527            // ===== Window functions =====
528            supports_window_exclude: false,
529            unnest_with_ordinality: true,
530            lowercase_window_frame_keywords: false,
531            normalize_window_frame_between: false,
532
533            // ===== Array =====
534            array_concat_is_var_len: true,
535            array_size_dim_required: None,
536            can_implement_array_any: false,
537            array_size_name: "ARRAY_LENGTH",
538
539            // ===== BETWEEN =====
540            supports_between_flags: false,
541
542            // ===== Boolean =====
543            is_bool_allowed: true,
544            ensure_bools: false,
545
546            // ===== EXTRACT =====
547            extract_allows_quotes: true,
548            normalize_extract_date_parts: false,
549
550            // ===== Other features =====
551            try_supported: true,
552            supports_uescape: true,
553            supports_to_number: true,
554            supports_single_arg_concat: true,
555            last_day_supports_date_part: true,
556            supports_exploding_projections: true,
557            supports_unix_seconds: false,
558            supports_like_quantifiers: true,
559            supports_decode_case: true,
560            set_op_modifiers: true,
561            update_statement_supports_from: true,
562
563            // ===== COLLATE =====
564            collate_is_func: false,
565
566            // ===== INSERT =====
567            duplicate_key_update_with_set: true,
568            insert_overwrite: " OVERWRITE TABLE",
569
570            // ===== RETURNING =====
571            returning_end: true,
572
573            // ===== MERGE =====
574            matched_by_source: true,
575
576            // ===== CREATE FUNCTION =====
577            create_function_return_as: true,
578            parameter_default_equals: false,
579
580            // ===== COMPUTED COLUMN =====
581            computed_column_with_type: true,
582
583            // ===== UNPIVOT =====
584            unpivot_aliases_are_identifiers: true,
585
586            // ===== STAR =====
587            star_except: "EXCEPT",
588
589            // ===== HEX =====
590            hex_func: "HEX",
591
592            // ===== WITH =====
593            with_properties_prefix: "WITH",
594
595            // ===== PAD =====
596            pad_fill_pattern_is_required: false,
597
598            // ===== INDEX =====
599            index_on: "ON",
600
601            // ===== GROUPING =====
602            groupings_sep: ",",
603
604            // ===== STRUCT =====
605            struct_delimiter: ("<", ">"),
606            struct_curly_brace_notation: false,
607            array_bracket_only: false,
608            struct_field_sep: " ",
609
610            // ===== EXCEPT/INTERSECT =====
611            except_intersect_support_all_clause: true,
612
613            // ===== PARAMETERS/PLACEHOLDERS =====
614            parameter_token: "@",
615            named_placeholder_token: ":",
616
617            // ===== DATA TYPES =====
618            data_type_specifiers_allowed: false,
619
620            // ===== COMMENT =====
621            schema_comment_with_eq: true,
622        }
623    }
624}
625
626/// SQL reserved keywords that require quoting when used as identifiers
627/// Based on ANSI SQL standards and common dialect-specific reserved words
628mod reserved_keywords {
629    use std::collections::HashSet;
630    use std::sync::LazyLock;
631
632    /// Standard SQL reserved keywords (ANSI SQL:2016)
633    pub static SQL_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
634        [
635            "all", "alter", "and", "any", "array", "as", "asc", "at", "authorization",
636            "begin", "between", "both", "by",
637            "case", "cast", "check", "collate", "column", "commit", "constraint", "create", "cross", "cube", "current", "current_date", "current_time", "current_timestamp", "current_user",
638            "default", "delete", "desc", "distinct", "drop",
639            "else", "end", "escape", "except", "execute", "exists", "external",
640            "false", "fetch", "filter", "for", "foreign", "from", "full", "function",
641            "grant", "group", "grouping",
642            "having",
643            "if", "in", "index", "inner", "insert", "intersect", "interval", "into", "is",
644            "join",
645            "key",
646            "leading", "left", "like", "limit", "local", "localtime", "localtimestamp",
647            "match", "merge",
648            "natural", "no", "not", "null",
649            "of", "offset", "on", "only", "or", "order", "outer", "over",
650            "partition", "primary", "procedure",
651            "range", "references", "right", "rollback", "rollup", "row", "rows",
652            "select", "session_user", "set", "some",
653            "table", "tablesample", "then", "to", "trailing", "true", "truncate",
654            "union", "unique", "unknown", "update", "user", "using",
655            "values", "view",
656            "when", "where", "window", "with",
657        ].into_iter().collect()
658    });
659
660    /// BigQuery-specific reserved keywords
661    /// Based on: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#reserved_keywords
662    pub static BIGQUERY_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
663        let mut set = SQL_RESERVED.clone();
664        set.extend([
665            "assert_rows_modified", "at", "contains", "cube", "current", "define", "enum",
666            "escape", "exclude", "following", "for", "groups", "hash", "ignore",
667            "lateral", "lookup", "new", "no", "nulls", "of", "over", "preceding",
668            "proto", "qualify", "recursive", "respect", "struct", "tablesample",
669            "treat", "unbounded", "unnest", "window", "within",
670        ]);
671        // BigQuery does NOT reserve these keywords - they can be used as identifiers unquoted
672        set.remove("grant");
673        set.remove("key");
674        set.remove("index");
675        set.remove("values");
676        set.remove("table");
677        set
678    });
679
680    /// MySQL-specific reserved keywords
681    pub static MYSQL_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
682        let mut set = SQL_RESERVED.clone();
683        set.extend([
684            "accessible", "add", "analyze", "asensitive", "before", "bigint", "binary",
685            "blob", "call", "cascade", "change", "char", "character", "condition",
686            "continue", "convert", "current_date", "current_time", "current_timestamp",
687            "current_user", "cursor", "database", "databases", "day_hour", "day_microsecond",
688            "day_minute", "day_second", "dec", "decimal", "declare", "delayed", "describe",
689            "deterministic", "distinctrow", "div", "double", "dual", "each", "elseif",
690            "enclosed", "escaped", "exit", "explain", "float", "float4", "float8", "force",
691            "get", "high_priority", "hour_microsecond", "hour_minute", "hour_second",
692            "ignore", "infile", "inout", "insensitive", "int", "int1", "int2", "int3",
693            "int4", "int8", "integer", "iterate", "keys", "kill", "leave", "linear",
694            "lines", "load", "lock", "long", "longblob", "longtext", "loop", "low_priority",
695            "master_ssl_verify_server_cert", "maxvalue", "mediumblob", "mediumint", "mediumtext",
696            "middleint", "minute_microsecond", "minute_second", "mod", "modifies", "no_write_to_binlog",
697            "numeric", "optimize", "option", "optionally", "out", "outfile", "precision",
698            "purge", "read", "reads", "real", "regexp", "release", "rename", "repeat",
699            "replace", "require", "resignal", "restrict", "return", "revoke", "rlike",
700            "schema", "schemas", "second_microsecond", "sensitive", "separator", "show",
701            "signal", "smallint", "spatial", "specific", "sql", "sql_big_result",
702            "sql_calc_found_rows", "sql_small_result", "sqlexception", "sqlstate", "sqlwarning",
703            "ssl", "starting", "straight_join", "terminated", "text", "tinyblob", "tinyint",
704            "tinytext", "trigger", "undo", "unlock", "unsigned", "usage", "utc_date",
705            "utc_time", "utc_timestamp", "varbinary", "varchar", "varcharacter", "varying",
706            "while", "write", "xor", "year_month", "zerofill",
707        ]);
708        set.remove("table");
709        set
710    });
711
712    /// Doris-specific reserved keywords
713    /// Extends MySQL reserved with additional Doris-specific words
714    pub static DORIS_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
715        let mut set = MYSQL_RESERVED.clone();
716        set.extend([
717            "aggregate", "anti", "array", "backend", "backup", "begin", "bitmap",
718            "boolean", "broker", "buckets", "cached", "cancel", "cast", "catalog",
719            "charset", "cluster", "collation", "columns", "comment", "commit",
720            "config", "connection", "count", "current", "data", "date", "datetime",
721            "day", "deferred", "distributed", "dynamic", "enable", "end",
722            "events", "export", "external", "fields", "first", "follower", "format",
723            "free", "frontend", "full", "functions", "global", "grants", "hash",
724            "help", "hour", "install", "intermediate", "json", "label", "last",
725            "less", "level", "link", "local", "location", "max", "merge",
726            "min", "minute", "modify", "month", "name", "names", "negative",
727            "nulls", "observer", "offset", "only", "open", "overwrite", "password",
728            "path", "plan", "plugin", "plugins", "policy", "process", "properties",
729            "property", "query", "quota", "recover", "refresh", "repair", "replica",
730            "repository", "resource", "restore", "resume", "role", "roles",
731            "rollback", "rollup", "routine", "sample", "second", "semi", "session",
732            "signed", "snapshot", "start", "stats", "status", "stop", "stream",
733            "string", "sum", "tables", "tablet", "temporary", "text", "timestamp",
734            "transaction", "trash", "trim", "truncate", "type", "user", "value",
735            "variables", "verbose", "version", "view", "warnings", "week", "work",
736            "year",
737        ]);
738        set
739    });
740
741    /// PostgreSQL-specific reserved keywords
742    pub static POSTGRES_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
743        let mut set = SQL_RESERVED.clone();
744        set.extend([
745            "analyse", "analyze", "asymmetric", "binary", "collation", "concurrently",
746            "current_catalog", "current_role", "current_schema", "deferrable", "do",
747            "freeze", "ilike", "initially", "isnull", "lateral", "notnull", "placing",
748            "returning", "similar", "symmetric", "variadic", "verbose",
749        ]);
750        // PostgreSQL doesn't require quoting for these keywords
751        set.remove("default");
752        set.remove("interval");
753        set.remove("match");
754        set.remove("offset");
755        set.remove("table");
756        set
757    });
758
759    /// Redshift-specific reserved keywords
760    /// Based on: https://docs.aws.amazon.com/redshift/latest/dg/r_pg_keywords.html
761    /// Note: `index` is NOT reserved in Redshift (unlike PostgreSQL)
762    pub static REDSHIFT_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
763        [
764            "aes128", "aes256", "all", "allowoverwrite", "analyse", "analyze", "and", "any",
765            "array", "as", "asc", "authorization", "az64", "backup", "between", "binary",
766            "blanksasnull", "both", "bytedict", "bzip2", "case", "cast", "check", "collate",
767            "column", "constraint", "create", "credentials", "cross", "current_date",
768            "current_time", "current_timestamp", "current_user", "current_user_id", "default",
769            "deferrable", "deflate", "defrag", "delta", "delta32k", "desc", "disable",
770            "distinct", "do", "else", "emptyasnull", "enable", "encode", "encrypt", "encryption",
771            "end", "except", "explicit", "false", "for", "foreign", "freeze", "from", "full",
772            "globaldict256", "globaldict64k", "grant", "group", "gzip", "having", "identity",
773            "ignore", "ilike", "in", "initially", "inner", "intersect", "interval", "into",
774            "is", "isnull", "join", "leading", "left", "like", "limit", "localtime",
775            "localtimestamp", "lun", "luns", "lzo", "lzop", "minus", "mostly16", "mostly32",
776            "mostly8", "natural", "new", "not", "notnull", "null", "nulls", "off", "offline",
777            "offset", "oid", "old", "on", "only", "open", "or", "order", "outer", "overlaps",
778            "parallel", "partition", "percent", "permissions", "pivot", "placing", "primary",
779            "raw", "readratio", "recover", "references", "rejectlog", "resort", "respect",
780            "restore", "right", "select", "session_user", "similar", "snapshot", "some",
781            "sysdate", "system", "table", "tag", "tdes", "text255", "text32k", "then",
782            "timestamp", "to", "top", "trailing", "true", "truncatecolumns", "type", "union",
783            "unique", "unnest", "unpivot", "user", "using", "verbose", "wallet", "when",
784            "where", "with", "without",
785        ].into_iter().collect()
786    });
787
788    /// DuckDB-specific reserved keywords
789    pub static DUCKDB_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
790        let mut set = POSTGRES_RESERVED.clone();
791        set.extend([
792            "anti", "asof", "columns", "describe", "groups", "macro",
793            "pivot", "pivot_longer", "pivot_wider", "qualify", "replace",
794            "respect", "semi", "show", "table", "unpivot",
795        ]);
796        set.remove("at");
797        set.remove("row");
798        set
799    });
800
801    /// Teradata-specific reserved keywords
802    pub static TERADATA_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
803        let mut set = SQL_RESERVED.clone();
804        set.extend([
805            "abort", "abortsession", "account", "activity", "add", "after", "algorithm",
806            "all", "allocate", "alter", "amp", "analyse", "analyze", "and", "ansidate",
807            "any", "are", "array", "as", "asc", "at", "authorization", "avg", "before",
808            "begin", "between", "bigint", "binary", "blob", "both", "bt", "but", "by",
809            "byte", "byteint", "bytes", "call", "cascade", "case", "casespecific",
810            "cast", "cd", "char", "character", "characters", "character_length",
811            "chars", "check", "checkpoint", "class", "clob", "close", "cluster",
812            "cm", "coalesce", "collation", "collect", "column", "comment", "commit",
813            "compress", "connect", "constraint", "constructor", "consume", "contains",
814            "continue", "convert", "copy", "correlation", "cos", "count", "create",
815            "cross", "cs", "csum", "current", "current_date", "current_time",
816            "current_timestamp", "cursor", "cv", "cycle", "data", "database", "date",
817            "dateform", "day", "deallocate", "dec", "decimal", "declare", "default",
818            "deferred", "degrees", "del", "delete", "desc", "describe", "descriptor",
819            "deterministic", "diagnostic", "disabled", "distinct", "do", "domain",
820            "double", "drop", "dual", "dump", "dynamic", "each", "else", "elseif",
821            "enabled", "end", "eq", "error", "errorfiles", "errortables", "escape",
822            "et", "except", "exception", "exclusive", "exec", "execute", "exists",
823            "exit", "exp", "explain", "external", "extract", "fallback", "false",
824            "fastexport", "fetch", "first", "float", "for", "force", "foreign",
825            "format", "found", "freespace", "from", "full", "function", "ge", "get",
826            "give", "global", "go", "goto", "grant", "graphic", "group", "gt",
827            "handler", "hash", "hashamp", "hashbakamp", "hashbucket", "hashrow",
828            "having", "help", "hour", "identity", "if", "immediate", "in", "index",
829            "indicator", "initiate", "inner", "inout", "input", "ins", "insert",
830            "instead", "int", "integer", "integerdate", "intersect", "interval",
831            "into", "is", "iterate", "join", "journal", "key", "kurtosis", "language",
832            "large", "le", "leading", "leave", "left", "level", "like", "limit",
833            "ln", "loading", "local", "locator", "lock", "locking", "log", "logging",
834            "logon", "long", "loop", "lower", "lt", "macro", "map", "match", "mavg",
835            "max", "maximum", "mcharacters", "mdiff", "merge", "method", "min",
836            "minimum", "minus", "minute", "mlinreg", "mload", "mod", "mode", "modifies",
837            "modify", "monitor", "monresource", "monsession", "month", "msubstr",
838            "msum", "multiset", "named", "names", "national", "natural", "nchar",
839            "nclob", "ne", "new", "next", "no", "none", "not", "nowait", "null",
840            "nullif", "nullifzero", "number", "numeric", "object", "objects", "octet_length",
841            "of", "off", "old", "on", "only", "open", "option", "or", "order", "ordinality",
842            "out", "outer", "output", "over", "overlaps", "override", "parameter", "password",
843            "percent", "perm", "permanent", "position", "precision", "prepare", "preserve",
844            "primary", "privileges", "procedure", "profile", "proportional", "protection",
845            "public", "qualified", "qualify", "quantile", "queue", "radians", "random",
846            "range_n", "rank", "reads", "real", "recursive", "references", "referencing",
847            "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2", "regr_slope",
848            "regr_sxx", "regr_sxy", "regr_syy", "relative", "release", "rename", "repeat",
849            "replace", "replication", "request", "resignal", "restart", "restore", "restrict",
850            "result", "resume", "retrieve", "return", "returns", "revert", "revoke", "right",
851            "rights", "role", "rollback", "rollforward", "rollup", "row", "row_number", "rowid",
852            "rows", "sample", "sampleid", "scroll", "second", "seconds", "sel", "select", "session",
853            "set", "setresrate", "sets", "setsessrate", "share", "show", "signal", "sin", "skew",
854            "smallint", "some", "source", "specific", "spool", "sql", "sqlexception", "sqlstate",
855            "sqltext", "sqlwarning", "sqrt", "ss", "start", "startup", "statement", "statistics",
856            "stddev_pop", "stddev_samp", "string", "subscriber", "subset", "substr", "substring",
857            "sum", "summary", "suspend", "system_user", "table", "tan", "then", "threshold",
858            "time", "timezone_hour", "timezone_minute", "title", "to", "top",
859            "trace", "trailing", "transaction", "translate", "translate_chk", "trigger", "trim",
860            "true", "type", "uc", "ud", "uescape", "undefined", "under", "undo", "union", "unique",
861            "until", "upd", "update", "upper", "upsert", "usage", "user", "using", "value", "values",
862            "var_pop", "var_samp", "varbyte", "varchar", "vargraphic", "varying", "view", "volatile",
863            "when", "where", "while", "width_bucket", "with", "without", "work", "write", "year",
864            "zeroifnull", "zone",
865        ]);
866        set
867    });
868
869    /// Presto/Trino/Athena-specific reserved keywords
870    pub static PRESTO_TRINO_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
871        let mut set = SQL_RESERVED.clone();
872        set.extend([
873            "alter", "and", "as", "between", "by", "case", "cast", "constraint", "create",
874            "cross", "cube", "current_catalog", "current_date", "current_path", "current_role",
875            "current_schema", "current_time", "current_timestamp", "current_user", "deallocate",
876            "delete", "describe", "distinct", "drop", "else", "end", "escape", "except",
877            "execute", "exists", "extract", "false", "for", "from", "full", "group", "grouping",
878            "having", "in", "inner", "insert", "intersect", "into", "is", "join", "json_array",
879            "json_exists", "json_object", "json_query", "json_table", "json_value", "left",
880            "like", "listagg", "localtime", "localtimestamp", "natural", "normalize", "not",
881            "null", "on", "or", "order", "outer", "prepare", "recursive", "right", "rollup",
882            "select", "skip", "table", "then", "trim", "true", "uescape", "union", "unnest",
883            "using", "values", "when", "where", "with",
884        ]);
885        set
886    });
887
888    /// StarRocks-specific reserved keywords
889    /// Based on: https://docs.starrocks.io/docs/sql-reference/sql-statements/keywords/#reserved-keywords
890    pub static STARROCKS_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
891        [
892            "add", "all", "alter", "analyze", "and", "array", "as", "asc",
893            "between", "bigint", "bitmap", "both", "by",
894            "case", "char", "character", "check", "collate", "column", "compaction",
895            "convert", "create", "cross", "cube", "current_date", "current_role",
896            "current_time", "current_timestamp", "current_user",
897            "database", "databases", "decimal", "decimalv2", "decimal32", "decimal64",
898            "decimal128", "default", "deferred", "delete", "dense_rank", "desc",
899            "describe", "distinct", "double", "drop", "dual",
900            "else", "except", "exists", "explain",
901            "false", "first_value", "float", "for", "force", "from", "full", "function",
902            "grant", "group", "grouping", "grouping_id", "groups",
903            "having", "hll", "host",
904            "if", "ignore", "immediate", "in", "index", "infile", "inner", "insert",
905            "int", "integer", "intersect", "into", "is",
906            "join", "json",
907            "key", "keys", "kill",
908            "lag", "largeint", "last_value", "lateral", "lead", "left", "like",
909            "limit", "load", "localtime", "localtimestamp",
910            "maxvalue", "minus", "mod",
911            "not", "ntile", "null",
912            "on", "or", "order", "outer", "outfile", "over",
913            "partition", "percentile", "primary", "procedure",
914            "qualify",
915            "range", "rank", "read", "regexp", "release", "rename", "replace",
916            "revoke", "right", "rlike", "row", "row_number", "rows",
917            "schema", "schemas", "select", "set", "set_var", "show", "smallint",
918            "system",
919            "table", "terminated", "text", "then", "tinyint", "to", "true",
920            "union", "unique", "unsigned", "update", "use", "using",
921            "values", "varchar",
922            "when", "where", "with",
923        ].into_iter().collect()
924    });
925
926    /// SingleStore-specific reserved keywords
927    /// Based on: https://docs.singlestore.com/cloud/reference/sql-reference/restricted-keywords/list-of-restricted-keywords/
928    pub static SINGLESTORE_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
929        let mut set = MYSQL_RESERVED.clone();
930        set.extend([
931            // Additional SingleStore reserved keywords from Python sqlglot
932            // NOTE: "all" is excluded because ORDER BY ALL needs ALL unquoted
933            "abs", "account", "acos", "adddate", "addtime", "admin", "aes_decrypt", "aes_encrypt",
934            "aggregate", "aggregates", "aggregator", "anti_join", "any_value",
935            "approx_count_distinct", "approx_percentile", "arrange", "arrangement", "asin",
936            "atan", "atan2", "attach", "autostats", "avro",
937            "background", "backup", "batch", "batches", "boot_strapping",
938            "ceil", "ceiling", "coercibility", "columnar", "columnstore", "compile", "concurrent",
939            "connection_id", "cos", "cot", "current_security_groups", "current_security_roles",
940            "dayname", "dayofmonth", "dayofweek", "dayofyear", "degrees",
941            "dot_product", "dump", "durability",
942            "earliest", "echo", "election", "euclidean_distance", "exp", "extractor", "extractors",
943            "floor", "foreground", "found_rows", "from_base64", "from_days", "from_unixtime", "fs",
944            "fulltext",
945            "gc", "gcs", "geography", "geography_area", "geography_contains", "geography_distance",
946            "geography_intersects", "geography_latitude", "geography_length", "geography_longitude",
947            "geographypoint", "geography_point", "geography_within_distance", "geometry",
948            "geometry_area", "geometry_contains", "geometry_distance", "geometry_filter",
949            "geometry_intersects", "geometry_length", "geometrypoint", "geometry_point",
950            "geometry_within_distance", "geometry_x", "geometry_y", "greatest", "groups",
951            "group_concat", "gzip",
952            "hdfs", "hex", "highlight",
953            "ifnull", "ilike", "inet_aton", "inet_ntoa", "inet6_aton", "inet6_ntoa", "initcap",
954            "instr", "interpreter_mode", "isnull",
955            "json", "json_agg", "json_array_contains_double", "json_array_contains_json",
956            "json_array_contains_string", "json_delete_key", "json_extract_double",
957            "json_extract_json", "json_extract_string", "json_extract_bigint", "json_get_type",
958            "json_length", "json_set_double", "json_set_json", "json_set_string",
959            "kafka",
960            "lag", "last_day", "last_insert_id", "latest", "lcase", "lead", "leaf", "least",
961            "leaves", "length", "license", "links", "llvm", "ln", "load", "locate", "log",
962            "log10", "log2", "lpad", "lz4",
963            "management", "match", "mbc", "md5", "median", "memsql", "memsql_deserialize",
964            "memsql_serialize", "metadata", "microsecond", "minute", "model", "monthname",
965            "months_between", "mpl",
966            "namespace", "node", "noparam", "now", "nth_value", "ntile", "nullcols", "nullif",
967            "object", "octet_length", "offsets", "online", "optimizer", "orphan",
968            "parquet", "partitions", "pause", "percentile_cont", "percentile_disc", "periodic",
969            "persisted", "pi", "pipeline", "pipelines", "plancache", "plugins", "pool", "pools",
970            "pow", "power", "process", "processlist", "profile", "profiles",
971            "quarter", "queries", "query",
972            "radians", "rand", "record", "reduce", "redundancy", "regexp_match", "regexp_substr",
973            "remote", "replication", "resource", "resource_pool", "restore", "retry", "role",
974            "roles", "round", "rpad", "rtrim", "running",
975            "s3", "scalar", "sec_to_time", "second", "security_lists_intersect", "semi_join",
976            "sha", "sha1", "sha2", "shard", "sharded", "sharded_id", "sigmoid", "sign", "sin",
977            "skip", "sleep", "snapshot", "soname", "sparse", "spatial_check_index", "split",
978            "sqrt", "standalone", "std", "stddev", "stddev_pop", "stddev_samp", "stop",
979            "str_to_date", "subdate", "substr", "substring_index", "success", "synchronize",
980            "table_checksum", "tan", "task", "timediff", "time_bucket", "time_format",
981            "time_to_sec", "timestampadd", "timestampdiff", "to_base64", "to_char", "to_date",
982            "to_days", "to_json", "to_number", "to_seconds", "to_timestamp", "tracelogs",
983            "transform", "trim", "trunc", "truncate",
984            "ucase", "unhex", "unix_timestamp", "utc_date", "utc_time", "utc_timestamp",
985            "vacuum", "variance", "var_pop", "var_samp", "vector_sub", "voting",
986            "week", "weekday", "weekofyear", "workload",
987            "year",
988        ]);
989        // Remove "all" because ORDER BY ALL needs ALL unquoted
990        set.remove("all");
991        set
992    });
993
994    /// SQLite-specific reserved keywords
995    /// SQLite has a very minimal set of reserved keywords - most things can be used as identifiers unquoted
996    /// Reference: https://www.sqlite.org/lang_keywords.html
997    pub static SQLITE_RESERVED: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
998        // SQLite only truly reserves these - everything else can be used as identifier unquoted
999        [
1000            "abort", "action", "add", "after", "all", "alter", "always", "analyze", "and", "as",
1001            "asc", "attach", "autoincrement", "before", "begin", "between", "by", "cascade", "case",
1002            "cast", "check", "collate", "column", "commit", "conflict", "constraint", "create", "cross",
1003            "current", "current_date", "current_time", "current_timestamp", "database", "default",
1004            "deferrable", "deferred", "delete", "desc", "detach", "distinct", "do", "drop", "each",
1005            "else", "end", "escape", "except", "exclude", "exclusive", "exists", "explain", "fail",
1006            "filter", "first", "following", "for", "foreign", "from", "full", "generated", "glob",
1007            "group", "groups", "having", "if", "ignore", "immediate", "in", "index", "indexed",
1008            "initially", "inner", "insert", "instead", "intersect", "into", "is", "isnull", "join",
1009            "key", "last", "left", "like", "limit", "natural", "no", "not", "nothing", "notnull",
1010            "null", "nulls", "of", "offset", "on", "or", "order", "others", "outer", "partition",
1011            "plan", "pragma", "preceding", "primary", "query", "raise", "range", "recursive",
1012            "references", "regexp", "reindex", "release", "rename", "replace", "restrict", "returning",
1013            "right", "rollback", "row", "rows", "savepoint", "select", "set", "table", "temp",
1014            "temporary", "then", "ties", "to", "transaction", "trigger", "unbounded", "union",
1015            "unique", "update", "using", "vacuum", "values", "view", "virtual", "when", "where",
1016            "window", "with", "without",
1017        ].into_iter().collect()
1018    });
1019}
1020
1021impl Generator {
1022    /// Create a new generator with the default configuration.
1023    ///
1024    /// Equivalent to `Generator::with_config(GeneratorConfig::default())`.
1025    /// Uses uppercase keywords, double-quote identifier quoting, no pretty-printing,
1026    /// and no dialect-specific transformations.
1027    pub fn new() -> Self {
1028        Self::with_config(GeneratorConfig::default())
1029    }
1030
1031    /// Create a generator with a custom [`GeneratorConfig`].
1032    ///
1033    /// Use this when you need dialect-specific output, pretty-printing, or other
1034    /// non-default settings.
1035    pub fn with_config(config: GeneratorConfig) -> Self {
1036        Self {
1037            config,
1038            output: String::new(),
1039            indent_level: 0,
1040            athena_hive_context: false,
1041            sqlite_inline_pk_columns: std::collections::HashSet::new(),
1042            merge_strip_qualifiers: Vec::new(),
1043        }
1044    }
1045
1046    /// Add column aliases to a query expression for TSQL SELECT INTO.
1047    /// This ensures that unaliased columns get explicit aliases (e.g., `a` -> `a AS a`).
1048    /// Recursively processes all SELECT expressions in the query tree.
1049    fn add_column_aliases_to_query(expr: Expression) -> Expression {
1050        match expr {
1051            Expression::Select(mut select) => {
1052                // Add aliases to all select expressions that don't already have them
1053                select.expressions = select.expressions
1054                    .into_iter()
1055                    .map(|e| Self::add_alias_to_expression(e))
1056                    .collect();
1057
1058                // Recursively process subqueries in FROM clause
1059                if let Some(ref mut from) = select.from {
1060                    from.expressions = from.expressions
1061                        .iter()
1062                        .cloned()
1063                        .map(|e| Self::add_column_aliases_to_query(e))
1064                        .collect();
1065                }
1066
1067                Expression::Select(select)
1068            }
1069            Expression::Subquery(mut sq) => {
1070                sq.this = Self::add_column_aliases_to_query(sq.this);
1071                Expression::Subquery(sq)
1072            }
1073            Expression::Paren(mut p) => {
1074                p.this = Self::add_column_aliases_to_query(p.this);
1075                Expression::Paren(p)
1076            }
1077            // For other expressions (Union, Intersect, etc.), pass through
1078            other => other,
1079        }
1080    }
1081
1082    /// Add an alias to a single select expression if it doesn't already have one.
1083    /// Returns the expression with alias (e.g., `a` -> `a AS a`).
1084    fn add_alias_to_expression(expr: Expression) -> Expression {
1085        use crate::expressions::Alias;
1086
1087        match &expr {
1088            // Already aliased - just return it
1089            Expression::Alias(_) => expr,
1090
1091            // Column reference: add alias from column name
1092            Expression::Column(col) => {
1093                Expression::Alias(Box::new(Alias {
1094                    this: expr.clone(),
1095                    alias: col.name.clone(),
1096                    column_aliases: Vec::new(),
1097                    pre_alias_comments: Vec::new(),
1098                    trailing_comments: Vec::new(),
1099                }))
1100            }
1101
1102            // Identifier: add alias from identifier name
1103            Expression::Identifier(ident) => {
1104                Expression::Alias(Box::new(Alias {
1105                    this: expr.clone(),
1106                    alias: ident.clone(),
1107                    column_aliases: Vec::new(),
1108                    pre_alias_comments: Vec::new(),
1109                    trailing_comments: Vec::new(),
1110                }))
1111            }
1112
1113            // Subquery: recursively process and add alias if inner returns a named column
1114            Expression::Subquery(sq) => {
1115                let processed = Self::add_column_aliases_to_query(Expression::Subquery(sq.clone()));
1116                // Subqueries that are already aliased keep their alias
1117                if sq.alias.is_some() {
1118                    processed
1119                } else {
1120                    // If there's no alias, keep it as-is (let TSQL handle it)
1121                    processed
1122                }
1123            }
1124
1125            // Star expressions (*) - don't alias
1126            Expression::Star(_) => expr,
1127
1128            // For other expressions, don't add an alias
1129            // (function calls, literals, etc. would need explicit aliases anyway)
1130            _ => expr,
1131        }
1132    }
1133
1134    /// Try to evaluate a constant arithmetic expression to a number literal.
1135    /// Returns the evaluated result if the expression is a constant arithmetic expression,
1136    /// otherwise returns the original expression.
1137    fn try_evaluate_constant(expr: &Expression) -> Option<i64> {
1138        match expr {
1139            Expression::Literal(Literal::Number(n)) => n.parse::<i64>().ok(),
1140            Expression::Add(op) => {
1141                let left = Self::try_evaluate_constant(&op.left)?;
1142                let right = Self::try_evaluate_constant(&op.right)?;
1143                Some(left + right)
1144            }
1145            Expression::Sub(op) => {
1146                let left = Self::try_evaluate_constant(&op.left)?;
1147                let right = Self::try_evaluate_constant(&op.right)?;
1148                Some(left - right)
1149            }
1150            Expression::Mul(op) => {
1151                let left = Self::try_evaluate_constant(&op.left)?;
1152                let right = Self::try_evaluate_constant(&op.right)?;
1153                Some(left * right)
1154            }
1155            Expression::Div(op) => {
1156                let left = Self::try_evaluate_constant(&op.left)?;
1157                let right = Self::try_evaluate_constant(&op.right)?;
1158                if right != 0 {
1159                    Some(left / right)
1160                } else {
1161                    None
1162                }
1163            }
1164            Expression::Paren(p) => Self::try_evaluate_constant(&p.this),
1165            _ => None,
1166        }
1167    }
1168
1169    /// Check if an identifier is a reserved keyword for the current dialect
1170    fn is_reserved_keyword(&self, name: &str) -> bool {
1171        use crate::dialects::DialectType;
1172        let lower = name.to_lowercase();
1173        let lower_ref = lower.as_str();
1174
1175        match self.config.dialect {
1176            Some(DialectType::BigQuery) => reserved_keywords::BIGQUERY_RESERVED.contains(lower_ref),
1177            Some(DialectType::MySQL) | Some(DialectType::TiDB) => {
1178                reserved_keywords::MYSQL_RESERVED.contains(lower_ref)
1179            }
1180            Some(DialectType::Doris) => {
1181                reserved_keywords::DORIS_RESERVED.contains(lower_ref)
1182            }
1183            Some(DialectType::SingleStore) => reserved_keywords::SINGLESTORE_RESERVED.contains(lower_ref),
1184            Some(DialectType::StarRocks) => reserved_keywords::STARROCKS_RESERVED.contains(lower_ref),
1185            Some(DialectType::PostgreSQL) | Some(DialectType::CockroachDB) | Some(DialectType::Materialize) | Some(DialectType::RisingWave) => {
1186                reserved_keywords::POSTGRES_RESERVED.contains(lower_ref)
1187            }
1188            Some(DialectType::Redshift) => reserved_keywords::REDSHIFT_RESERVED.contains(lower_ref),
1189            // Snowflake: Python sqlglot has RESERVED_KEYWORDS = set() for Snowflake,
1190            // meaning it never quotes identifiers based on reserved word status.
1191            Some(DialectType::Snowflake) => false,
1192            // ClickHouse: don't quote reserved keywords to preserve identity output
1193            Some(DialectType::ClickHouse) => false,
1194            Some(DialectType::DuckDB) => reserved_keywords::DUCKDB_RESERVED.contains(lower_ref),
1195            Some(DialectType::Teradata) => reserved_keywords::TERADATA_RESERVED.contains(lower_ref),
1196            // TSQL, Fabric, Oracle, Spark, Hive, Solr: Python sqlglot has no RESERVED_KEYWORDS for these dialects, so don't quote identifiers
1197            Some(DialectType::TSQL) | Some(DialectType::Fabric) |
1198            Some(DialectType::Oracle) |
1199            Some(DialectType::Spark) | Some(DialectType::Databricks) |
1200            Some(DialectType::Hive) | Some(DialectType::Solr) => false,
1201            Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => {
1202                reserved_keywords::PRESTO_TRINO_RESERVED.contains(lower_ref)
1203            }
1204            Some(DialectType::SQLite) => reserved_keywords::SQLITE_RESERVED.contains(lower_ref),
1205            // For Generic dialect or None, don't add extra quoting to preserve identity
1206            Some(DialectType::Generic) | None => false,
1207            // For other dialects, use standard SQL reserved keywords
1208            _ => reserved_keywords::SQL_RESERVED.contains(lower_ref),
1209        }
1210    }
1211
1212    /// Normalize function name based on dialect settings
1213    fn normalize_func_name(&self, name: &str) -> String {
1214        match self.config.normalize_functions {
1215            NormalizeFunctions::Upper => name.to_uppercase(),
1216            NormalizeFunctions::Lower => name.to_lowercase(),
1217            NormalizeFunctions::None => name.to_string(),
1218        }
1219    }
1220
1221    /// Generate a SQL string from an AST expression.
1222    ///
1223    /// This is the primary generation method. It clears any previous internal state,
1224    /// walks the expression tree, and returns the resulting SQL text. The output
1225    /// respects the [`GeneratorConfig`] that was supplied at construction time.
1226    ///
1227    /// The generator can be reused across multiple calls; each call to `generate`
1228    /// resets the internal buffer.
1229    pub fn generate(&mut self, expr: &Expression) -> Result<String> {
1230        self.output.clear();
1231        self.generate_expression(expr)?;
1232        Ok(std::mem::take(&mut self.output))
1233    }
1234
1235    /// Convenience: generate SQL with the default configuration (no dialect, compact output).
1236    ///
1237    /// This is a static helper that creates a throwaway `Generator` internally.
1238    /// For repeated generation, prefer constructing a `Generator` once and calling
1239    /// [`generate`](Self::generate) on it.
1240    pub fn sql(expr: &Expression) -> Result<String> {
1241        let mut gen = Generator::new();
1242        gen.generate(expr)
1243    }
1244
1245    /// Convenience: generate SQL with pretty-printing enabled (indented, multi-line).
1246    ///
1247    /// Produces human-readable output with newlines and indentation. A trailing
1248    /// semicolon is appended automatically if not already present.
1249    pub fn pretty_sql(expr: &Expression) -> Result<String> {
1250        let config = GeneratorConfig {
1251            pretty: true,
1252            ..Default::default()
1253        };
1254        let mut gen = Generator::with_config(config);
1255        let mut sql = gen.generate(expr)?;
1256        // Add semicolon for pretty output
1257        if !sql.ends_with(';') {
1258            sql.push(';');
1259        }
1260        Ok(sql)
1261    }
1262
1263    fn generate_expression(&mut self, expr: &Expression) -> Result<()> {
1264        match expr {
1265            Expression::Select(select) => self.generate_select(select),
1266            Expression::Union(union) => self.generate_union(union),
1267            Expression::Intersect(intersect) => self.generate_intersect(intersect),
1268            Expression::Except(except) => self.generate_except(except),
1269            Expression::Insert(insert) => self.generate_insert(insert),
1270            Expression::Update(update) => self.generate_update(update),
1271            Expression::Delete(delete) => self.generate_delete(delete),
1272            Expression::Literal(lit) => self.generate_literal(lit),
1273            Expression::Boolean(b) => self.generate_boolean(b),
1274            Expression::Null(_) => {
1275                self.write_keyword("NULL");
1276                Ok(())
1277            }
1278            Expression::Identifier(id) => self.generate_identifier(id),
1279            Expression::Column(col) => self.generate_column(col),
1280            Expression::Pseudocolumn(pc) => self.generate_pseudocolumn(pc),
1281            Expression::Connect(c) => self.generate_connect_expr(c),
1282            Expression::Prior(p) => self.generate_prior(p),
1283            Expression::ConnectByRoot(cbr) => self.generate_connect_by_root(cbr),
1284            Expression::MatchRecognize(mr) => self.generate_match_recognize(mr),
1285            Expression::Table(table) => self.generate_table(table),
1286            Expression::StageReference(sr) => self.generate_stage_reference(sr),
1287            Expression::HistoricalData(hd) => self.generate_historical_data(hd),
1288            Expression::JoinedTable(jt) => self.generate_joined_table(jt),
1289            Expression::Star(star) => self.generate_star(star),
1290            Expression::BracedWildcard(expr) => self.generate_braced_wildcard(expr),
1291            Expression::Alias(alias) => self.generate_alias(alias),
1292            Expression::Cast(cast) => self.generate_cast(cast),
1293            Expression::Collation(coll) => self.generate_collation(coll),
1294            Expression::Case(case) => self.generate_case(case),
1295            Expression::Function(func) => self.generate_function(func),
1296            Expression::AggregateFunction(func) => self.generate_aggregate_function(func),
1297            Expression::WindowFunction(wf) => self.generate_window_function(wf),
1298            Expression::WithinGroup(wg) => self.generate_within_group(wg),
1299            Expression::Interval(interval) => self.generate_interval(interval),
1300
1301            // String functions
1302            Expression::ConcatWs(f) => self.generate_concat_ws(f),
1303            Expression::Substring(f) => self.generate_substring(f),
1304            Expression::Upper(f) => self.generate_unary_func("UPPER", f),
1305            Expression::Lower(f) => self.generate_unary_func("LOWER", f),
1306            Expression::Length(f) => self.generate_unary_func("LENGTH", f),
1307            Expression::Trim(f) => self.generate_trim(f),
1308            Expression::LTrim(f) => self.generate_simple_func("LTRIM", &f.this),
1309            Expression::RTrim(f) => self.generate_simple_func("RTRIM", &f.this),
1310            Expression::Replace(f) => self.generate_replace(f),
1311            Expression::Reverse(f) => self.generate_simple_func("REVERSE", &f.this),
1312            Expression::Left(f) => self.generate_left_right("LEFT", f),
1313            Expression::Right(f) => self.generate_left_right("RIGHT", f),
1314            Expression::Repeat(f) => self.generate_repeat(f),
1315            Expression::Lpad(f) => self.generate_pad("LPAD", f),
1316            Expression::Rpad(f) => self.generate_pad("RPAD", f),
1317            Expression::Split(f) => self.generate_split(f),
1318            Expression::RegexpLike(f) => self.generate_regexp_like(f),
1319            Expression::RegexpReplace(f) => self.generate_regexp_replace(f),
1320            Expression::RegexpExtract(f) => self.generate_regexp_extract(f),
1321            Expression::Overlay(f) => self.generate_overlay(f),
1322
1323            // Math functions
1324            Expression::Abs(f) => self.generate_simple_func("ABS", &f.this),
1325            Expression::Round(f) => self.generate_round(f),
1326            Expression::Floor(f) => self.generate_floor(f),
1327            Expression::Ceil(f) => self.generate_ceil(f),
1328            Expression::Power(f) => self.generate_power(f),
1329            Expression::Sqrt(f) => self.generate_sqrt_cbrt(f, "SQRT", "|/"),
1330            Expression::Cbrt(f) => self.generate_sqrt_cbrt(f, "CBRT", "||/"),
1331            Expression::Ln(f) => self.generate_simple_func("LN", &f.this),
1332            Expression::Log(f) => self.generate_log(f),
1333            Expression::Exp(f) => self.generate_simple_func("EXP", &f.this),
1334            Expression::Sign(f) => self.generate_simple_func("SIGN", &f.this),
1335            Expression::Greatest(f) => self.generate_vararg_func("GREATEST", &f.expressions),
1336            Expression::Least(f) => self.generate_vararg_func("LEAST", &f.expressions),
1337
1338            // Date/time functions
1339            Expression::CurrentDate(_) => {
1340                self.write_keyword("CURRENT_DATE");
1341                Ok(())
1342            }
1343            Expression::CurrentTime(f) => self.generate_current_time(f),
1344            Expression::CurrentTimestamp(f) => self.generate_current_timestamp(f),
1345            Expression::AtTimeZone(f) => self.generate_at_time_zone(f),
1346            Expression::DateAdd(f) => self.generate_date_add(f, "DATE_ADD"),
1347            Expression::DateSub(f) => self.generate_date_add(f, "DATE_SUB"),
1348            Expression::DateDiff(f) => self.generate_datediff(f),
1349            Expression::DateTrunc(f) => self.generate_date_trunc(f),
1350            Expression::Extract(f) => self.generate_extract(f),
1351            Expression::ToDate(f) => self.generate_to_date(f),
1352            Expression::ToTimestamp(f) => self.generate_to_timestamp(f),
1353
1354            // Control flow functions
1355            Expression::Coalesce(f) => {
1356                // Use original function name if preserved (COALESCE, IFNULL)
1357                let func_name = f.original_name.as_deref().unwrap_or("COALESCE");
1358                self.generate_vararg_func(func_name, &f.expressions)
1359            }
1360            Expression::NullIf(f) => self.generate_binary_func("NULLIF", &f.this, &f.expression),
1361            Expression::IfFunc(f) => self.generate_if_func(f),
1362            Expression::IfNull(f) => self.generate_ifnull(f),
1363            Expression::Nvl(f) => self.generate_nvl(f),
1364            Expression::Nvl2(f) => self.generate_nvl2(f),
1365
1366            // Type conversion
1367            Expression::TryCast(cast) => self.generate_try_cast(cast),
1368            Expression::SafeCast(cast) => self.generate_safe_cast(cast),
1369
1370            // Typed aggregate functions
1371            Expression::Count(f) => self.generate_count(f),
1372            Expression::Sum(f) => self.generate_agg_func("SUM", f),
1373            Expression::Avg(f) => self.generate_agg_func("AVG", f),
1374            Expression::Min(f) => self.generate_agg_func("MIN", f),
1375            Expression::Max(f) => self.generate_agg_func("MAX", f),
1376            Expression::GroupConcat(f) => self.generate_group_concat(f),
1377            Expression::StringAgg(f) => self.generate_string_agg(f),
1378            Expression::ListAgg(f) => self.generate_listagg(f),
1379            Expression::ArrayAgg(f) => {
1380                // Allow cross-dialect transforms to override the function name
1381                // (e.g., COLLECT_LIST for Spark)
1382                let override_name = f.name.as_ref()
1383                    .filter(|n| n.to_uppercase() != "ARRAY_AGG")
1384                    .map(|n| n.to_uppercase());
1385                match override_name {
1386                    Some(name) => self.generate_agg_func(&name, f),
1387                    None => self.generate_agg_func("ARRAY_AGG", f),
1388                }
1389            }
1390            Expression::ArrayConcatAgg(f) => self.generate_agg_func("ARRAY_CONCAT_AGG", f),
1391            Expression::CountIf(f) => self.generate_agg_func("COUNT_IF", f),
1392            Expression::SumIf(f) => self.generate_sum_if(f),
1393            Expression::Stddev(f) => self.generate_agg_func("STDDEV", f),
1394            Expression::StddevPop(f) => self.generate_agg_func("STDDEV_POP", f),
1395            Expression::StddevSamp(f) => self.generate_stddev_samp(f),
1396            Expression::Variance(f) => self.generate_agg_func("VARIANCE", f),
1397            Expression::VarPop(f) => {
1398                let name = if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
1399                    "VARIANCE_POP"
1400                } else {
1401                    "VAR_POP"
1402                };
1403                self.generate_agg_func(name, f)
1404            }
1405            Expression::VarSamp(f) => self.generate_agg_func("VAR_SAMP", f),
1406            Expression::Skewness(f) => {
1407                let name = match self.config.dialect {
1408                    Some(DialectType::Snowflake) => "SKEW",
1409                    _ => "SKEWNESS",
1410                };
1411                self.generate_agg_func(name, f)
1412            }
1413            Expression::Median(f) => self.generate_agg_func("MEDIAN", f),
1414            Expression::Mode(f) => self.generate_agg_func("MODE", f),
1415            Expression::First(f) => self.generate_agg_func("FIRST", f),
1416            Expression::Last(f) => self.generate_agg_func("LAST", f),
1417            Expression::AnyValue(f) => self.generate_agg_func("ANY_VALUE", f),
1418            Expression::ApproxDistinct(f) => {
1419                match self.config.dialect {
1420                    Some(DialectType::Hive) | Some(DialectType::Spark)
1421                    | Some(DialectType::Databricks) | Some(DialectType::BigQuery) => {
1422                        // These dialects use APPROX_COUNT_DISTINCT (single arg only)
1423                        self.generate_agg_func("APPROX_COUNT_DISTINCT", f)
1424                    }
1425                    Some(DialectType::Redshift) => {
1426                        // Redshift uses APPROXIMATE COUNT(DISTINCT expr)
1427                        self.write_keyword("APPROXIMATE COUNT");
1428                        self.write("(");
1429                        self.write_keyword("DISTINCT");
1430                        self.write(" ");
1431                        self.generate_expression(&f.this)?;
1432                        self.write(")");
1433                        Ok(())
1434                    }
1435                    _ => self.generate_agg_func("APPROX_DISTINCT", f),
1436                }
1437            },
1438            Expression::ApproxCountDistinct(f) => self.generate_agg_func("APPROX_COUNT_DISTINCT", f),
1439            Expression::ApproxPercentile(f) => self.generate_approx_percentile(f),
1440            Expression::Percentile(f) => self.generate_percentile("PERCENTILE", f),
1441            Expression::LogicalAnd(f) => {
1442                let name = match self.config.dialect {
1443                    Some(DialectType::Snowflake) => "BOOLAND_AGG",
1444                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::PostgreSQL) | Some(DialectType::DuckDB) | Some(DialectType::Redshift) => "BOOL_AND",
1445                    Some(DialectType::Oracle) | Some(DialectType::SQLite) | Some(DialectType::MySQL) => "MIN",
1446                    _ => "BOOL_AND",
1447                };
1448                self.generate_agg_func(name, f)
1449            }
1450            Expression::LogicalOr(f) => {
1451                let name = match self.config.dialect {
1452                    Some(DialectType::Snowflake) => "BOOLOR_AGG",
1453                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::PostgreSQL) | Some(DialectType::DuckDB) | Some(DialectType::Redshift) => "BOOL_OR",
1454                    Some(DialectType::Oracle) | Some(DialectType::SQLite) | Some(DialectType::MySQL) => "MAX",
1455                    _ => "BOOL_OR",
1456                };
1457                self.generate_agg_func(name, f)
1458            }
1459
1460            // Typed window functions
1461            Expression::RowNumber(_) => {
1462                if self.config.dialect == Some(DialectType::ClickHouse) {
1463                    self.write("row_number");
1464                } else {
1465                    self.write_keyword("ROW_NUMBER");
1466                }
1467                self.write("()");
1468                Ok(())
1469            }
1470            Expression::Rank(r) => {
1471                self.write_keyword("RANK");
1472                self.write("(");
1473                // Oracle hypothetical rank args: RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
1474                if !r.args.is_empty() {
1475                    for (i, arg) in r.args.iter().enumerate() {
1476                        if i > 0 { self.write(", "); }
1477                        self.generate_expression(arg)?;
1478                    }
1479                } else if let Some(order_by) = &r.order_by {
1480                    // DuckDB: RANK(ORDER BY col)
1481                    self.write_keyword(" ORDER BY ");
1482                    for (i, ob) in order_by.iter().enumerate() {
1483                        if i > 0 { self.write(", "); }
1484                        self.generate_ordered(ob)?;
1485                    }
1486                }
1487                self.write(")");
1488                Ok(())
1489            }
1490            Expression::DenseRank(dr) => {
1491                self.write_keyword("DENSE_RANK");
1492                self.write("(");
1493                // Oracle hypothetical rank args: DENSE_RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
1494                for (i, arg) in dr.args.iter().enumerate() {
1495                    if i > 0 { self.write(", "); }
1496                    self.generate_expression(arg)?;
1497                }
1498                self.write(")");
1499                Ok(())
1500            }
1501            Expression::NTile(f) => self.generate_ntile(f),
1502            Expression::Lead(f) => self.generate_lead_lag("LEAD", f),
1503            Expression::Lag(f) => self.generate_lead_lag("LAG", f),
1504            Expression::FirstValue(f) => self.generate_value_func("FIRST_VALUE", f),
1505            Expression::LastValue(f) => self.generate_value_func("LAST_VALUE", f),
1506            Expression::NthValue(f) => self.generate_nth_value(f),
1507            Expression::PercentRank(pr) => {
1508                self.write_keyword("PERCENT_RANK");
1509                self.write("(");
1510                // Oracle hypothetical rank args: PERCENT_RANK(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
1511                if !pr.args.is_empty() {
1512                    for (i, arg) in pr.args.iter().enumerate() {
1513                        if i > 0 { self.write(", "); }
1514                        self.generate_expression(arg)?;
1515                    }
1516                } else if let Some(order_by) = &pr.order_by {
1517                    // DuckDB: PERCENT_RANK(ORDER BY col)
1518                    self.write_keyword(" ORDER BY ");
1519                    for (i, ob) in order_by.iter().enumerate() {
1520                        if i > 0 { self.write(", "); }
1521                        self.generate_ordered(ob)?;
1522                    }
1523                }
1524                self.write(")");
1525                Ok(())
1526            }
1527            Expression::CumeDist(cd) => {
1528                self.write_keyword("CUME_DIST");
1529                self.write("(");
1530                // Oracle hypothetical rank args: CUME_DIST(val1, val2, ...) WITHIN GROUP (ORDER BY ...)
1531                if !cd.args.is_empty() {
1532                    for (i, arg) in cd.args.iter().enumerate() {
1533                        if i > 0 { self.write(", "); }
1534                        self.generate_expression(arg)?;
1535                    }
1536                } else if let Some(order_by) = &cd.order_by {
1537                    // DuckDB: CUME_DIST(ORDER BY col)
1538                    self.write_keyword(" ORDER BY ");
1539                    for (i, ob) in order_by.iter().enumerate() {
1540                        if i > 0 { self.write(", "); }
1541                        self.generate_ordered(ob)?;
1542                    }
1543                }
1544                self.write(")");
1545                Ok(())
1546            }
1547            Expression::PercentileCont(f) => self.generate_percentile("PERCENTILE_CONT", f),
1548            Expression::PercentileDisc(f) => self.generate_percentile("PERCENTILE_DISC", f),
1549
1550            // Additional string functions
1551            Expression::Contains(f) => self.generate_binary_func("CONTAINS", &f.this, &f.expression),
1552            Expression::StartsWith(f) => {
1553                let name = match self.config.dialect {
1554                    Some(DialectType::Spark) | Some(DialectType::Databricks) => "STARTSWITH",
1555                    _ => "STARTS_WITH",
1556                };
1557                self.generate_binary_func(name, &f.this, &f.expression)
1558            },
1559            Expression::EndsWith(f) => {
1560                let name = match self.config.dialect {
1561                    Some(DialectType::Snowflake) => "ENDSWITH",
1562                    Some(DialectType::Spark) | Some(DialectType::Databricks) => "ENDSWITH",
1563                    Some(DialectType::ClickHouse) => "endsWith",
1564                    _ => "ENDS_WITH",
1565                };
1566                self.generate_binary_func(name, &f.this, &f.expression)
1567            }
1568            Expression::Position(f) => self.generate_position(f),
1569            Expression::Initcap(f) => {
1570                match self.config.dialect {
1571                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => {
1572                        self.write_keyword("REGEXP_REPLACE");
1573                        self.write("(");
1574                        self.generate_expression(&f.this)?;
1575                        self.write(", '(\\w)(\\w*)', x -> UPPER(x[1]) || LOWER(x[2]))");
1576                        Ok(())
1577                    }
1578                    _ => self.generate_simple_func("INITCAP", &f.this),
1579                }
1580            },
1581            Expression::Ascii(f) => self.generate_simple_func("ASCII", &f.this),
1582            Expression::Chr(f) => self.generate_simple_func("CHR", &f.this),
1583            Expression::CharFunc(f) => self.generate_char_func(f),
1584            Expression::Soundex(f) => self.generate_simple_func("SOUNDEX", &f.this),
1585            Expression::Levenshtein(f) => self.generate_binary_func("LEVENSHTEIN", &f.this, &f.expression),
1586
1587            // Additional math functions
1588            Expression::ModFunc(f) => self.generate_mod_func(f),
1589            Expression::Random(_) => { self.write_keyword("RANDOM"); self.write("()"); Ok(()) }
1590            Expression::Rand(f) => self.generate_rand(f),
1591            Expression::TruncFunc(f) => self.generate_truncate_func(f),
1592            Expression::Pi(_) => { self.write_keyword("PI"); self.write("()"); Ok(()) }
1593            Expression::Radians(f) => self.generate_simple_func("RADIANS", &f.this),
1594            Expression::Degrees(f) => self.generate_simple_func("DEGREES", &f.this),
1595            Expression::Sin(f) => self.generate_simple_func("SIN", &f.this),
1596            Expression::Cos(f) => self.generate_simple_func("COS", &f.this),
1597            Expression::Tan(f) => self.generate_simple_func("TAN", &f.this),
1598            Expression::Asin(f) => self.generate_simple_func("ASIN", &f.this),
1599            Expression::Acos(f) => self.generate_simple_func("ACOS", &f.this),
1600            Expression::Atan(f) => self.generate_simple_func("ATAN", &f.this),
1601            Expression::Atan2(f) => {
1602                let name = f.original_name.as_deref().unwrap_or("ATAN2");
1603                self.generate_binary_func(name, &f.this, &f.expression)
1604            }
1605
1606            // Control flow
1607            Expression::Decode(f) => self.generate_decode(f),
1608
1609            // Additional date/time functions
1610            Expression::DateFormat(f) => self.generate_date_format("DATE_FORMAT", f),
1611            Expression::FormatDate(f) => self.generate_date_format("FORMAT_DATE", f),
1612            Expression::Year(f) => self.generate_simple_func("YEAR", &f.this),
1613            Expression::Month(f) => self.generate_simple_func("MONTH", &f.this),
1614            Expression::Day(f) => self.generate_simple_func("DAY", &f.this),
1615            Expression::Hour(f) => self.generate_simple_func("HOUR", &f.this),
1616            Expression::Minute(f) => self.generate_simple_func("MINUTE", &f.this),
1617            Expression::Second(f) => self.generate_simple_func("SECOND", &f.this),
1618            Expression::DayOfWeek(f) => {
1619                let name = match self.config.dialect {
1620                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => "DAY_OF_WEEK",
1621                    Some(DialectType::DuckDB) => "ISODOW",
1622                    _ => "DAYOFWEEK",
1623                };
1624                self.generate_simple_func(name, &f.this)
1625            }
1626            Expression::DayOfMonth(f) => {
1627                let name = match self.config.dialect {
1628                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => "DAY_OF_MONTH",
1629                    _ => "DAYOFMONTH",
1630                };
1631                self.generate_simple_func(name, &f.this)
1632            }
1633            Expression::DayOfYear(f) => {
1634                let name = match self.config.dialect {
1635                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => "DAY_OF_YEAR",
1636                    _ => "DAYOFYEAR",
1637                };
1638                self.generate_simple_func(name, &f.this)
1639            }
1640            Expression::WeekOfYear(f) => {
1641                // Python sqlglot default is WEEK_OF_YEAR; Hive/DuckDB/Spark/MySQL override to WEEKOFYEAR
1642                let name = match self.config.dialect {
1643                    Some(DialectType::Hive) | Some(DialectType::DuckDB) | Some(DialectType::Spark)
1644                    | Some(DialectType::Databricks) | Some(DialectType::MySQL) => "WEEKOFYEAR",
1645                    _ => "WEEK_OF_YEAR",
1646                };
1647                self.generate_simple_func(name, &f.this)
1648            }
1649            Expression::Quarter(f) => self.generate_simple_func("QUARTER", &f.this),
1650            Expression::AddMonths(f) => self.generate_binary_func("ADD_MONTHS", &f.this, &f.expression),
1651            Expression::MonthsBetween(f) => self.generate_binary_func("MONTHS_BETWEEN", &f.this, &f.expression),
1652            Expression::LastDay(f) => self.generate_last_day(f),
1653            Expression::NextDay(f) => self.generate_binary_func("NEXT_DAY", &f.this, &f.expression),
1654            Expression::Epoch(f) => self.generate_simple_func("EPOCH", &f.this),
1655            Expression::EpochMs(f) => self.generate_simple_func("EPOCH_MS", &f.this),
1656            Expression::FromUnixtime(f) => self.generate_from_unixtime(f),
1657            Expression::UnixTimestamp(f) => self.generate_unix_timestamp(f),
1658            Expression::MakeDate(f) => self.generate_make_date(f),
1659            Expression::MakeTimestamp(f) => self.generate_make_timestamp(f),
1660            Expression::TimestampTrunc(f) => self.generate_date_trunc(f),
1661
1662            // Array functions
1663            Expression::ArrayFunc(f) => self.generate_array_constructor(f),
1664            Expression::ArrayLength(f) => self.generate_simple_func("ARRAY_LENGTH", &f.this),
1665            Expression::ArraySize(f) => self.generate_simple_func("ARRAY_SIZE", &f.this),
1666            Expression::Cardinality(f) => self.generate_simple_func("CARDINALITY", &f.this),
1667            Expression::ArrayContains(f) => self.generate_binary_func("ARRAY_CONTAINS", &f.this, &f.expression),
1668            Expression::ArrayPosition(f) => self.generate_binary_func("ARRAY_POSITION", &f.this, &f.expression),
1669            Expression::ArrayAppend(f) => self.generate_binary_func("ARRAY_APPEND", &f.this, &f.expression),
1670            Expression::ArrayPrepend(f) => self.generate_binary_func("ARRAY_PREPEND", &f.this, &f.expression),
1671            Expression::ArrayConcat(f) => self.generate_vararg_func("ARRAY_CONCAT", &f.expressions),
1672            Expression::ArraySort(f) => self.generate_array_sort(f),
1673            Expression::ArrayReverse(f) => self.generate_simple_func("ARRAY_REVERSE", &f.this),
1674            Expression::ArrayDistinct(f) => self.generate_simple_func("ARRAY_DISTINCT", &f.this),
1675            Expression::ArrayJoin(f) => self.generate_array_join("ARRAY_JOIN", f),
1676            Expression::ArrayToString(f) => self.generate_array_join("ARRAY_TO_STRING", f),
1677            Expression::Unnest(f) => self.generate_unnest(f),
1678            Expression::Explode(f) => self.generate_simple_func("EXPLODE", &f.this),
1679            Expression::ExplodeOuter(f) => self.generate_simple_func("EXPLODE_OUTER", &f.this),
1680            Expression::ArrayFilter(f) => self.generate_array_filter(f),
1681            Expression::ArrayTransform(f) => self.generate_array_transform(f),
1682            Expression::ArrayFlatten(f) => self.generate_simple_func("FLATTEN", &f.this),
1683            Expression::ArrayCompact(f) => self.generate_simple_func("ARRAY_COMPACT", &f.this),
1684            Expression::ArrayIntersect(f) => self.generate_binary_func("ARRAY_INTERSECT", &f.this, &f.expression),
1685            Expression::ArrayUnion(f) => self.generate_binary_func("ARRAY_UNION", &f.this, &f.expression),
1686            Expression::ArrayExcept(f) => self.generate_binary_func("ARRAY_EXCEPT", &f.this, &f.expression),
1687            Expression::ArrayRemove(f) => self.generate_binary_func("ARRAY_REMOVE", &f.this, &f.expression),
1688            Expression::ArrayZip(f) => self.generate_vararg_func("ARRAYS_ZIP", &f.expressions),
1689            Expression::Sequence(f) => self.generate_sequence("SEQUENCE", f),
1690            Expression::Generate(f) => self.generate_sequence("GENERATE_SERIES", f),
1691
1692            // Struct functions
1693            Expression::StructFunc(f) => self.generate_struct_constructor(f),
1694            Expression::StructExtract(f) => self.generate_struct_extract(f),
1695            Expression::NamedStruct(f) => self.generate_named_struct(f),
1696
1697            // Map functions
1698            Expression::MapFunc(f) => self.generate_map_constructor(f),
1699            Expression::MapFromEntries(f) => self.generate_simple_func("MAP_FROM_ENTRIES", &f.this),
1700            Expression::MapFromArrays(f) => self.generate_binary_func("MAP_FROM_ARRAYS", &f.this, &f.expression),
1701            Expression::MapKeys(f) => self.generate_simple_func("MAP_KEYS", &f.this),
1702            Expression::MapValues(f) => self.generate_simple_func("MAP_VALUES", &f.this),
1703            Expression::MapContainsKey(f) => self.generate_binary_func("MAP_CONTAINS_KEY", &f.this, &f.expression),
1704            Expression::MapConcat(f) => self.generate_vararg_func("MAP_CONCAT", &f.expressions),
1705            Expression::ElementAt(f) => self.generate_binary_func("ELEMENT_AT", &f.this, &f.expression),
1706            Expression::TransformKeys(f) => self.generate_transform_func("TRANSFORM_KEYS", f),
1707            Expression::TransformValues(f) => self.generate_transform_func("TRANSFORM_VALUES", f),
1708
1709            // JSON functions
1710            Expression::JsonExtract(f) => self.generate_json_extract("JSON_EXTRACT", f),
1711            Expression::JsonExtractScalar(f) => self.generate_json_extract("JSON_EXTRACT_SCALAR", f),
1712            Expression::JsonExtractPath(f) => self.generate_json_path("JSON_EXTRACT_PATH", f),
1713            Expression::JsonArray(f) => self.generate_vararg_func("JSON_ARRAY", &f.expressions),
1714            Expression::JsonObject(f) => self.generate_json_object(f),
1715            Expression::JsonQuery(f) => self.generate_json_extract("JSON_QUERY", f),
1716            Expression::JsonValue(f) => self.generate_json_extract("JSON_VALUE", f),
1717            Expression::JsonArrayLength(f) => self.generate_simple_func("JSON_ARRAY_LENGTH", &f.this),
1718            Expression::JsonKeys(f) => self.generate_simple_func("JSON_KEYS", &f.this),
1719            Expression::JsonType(f) => self.generate_simple_func("JSON_TYPE", &f.this),
1720            Expression::ParseJson(f) => {
1721                let name = match self.config.dialect {
1722                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => "JSON_PARSE",
1723                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
1724                        // PostgreSQL: CAST(x AS JSON)
1725                        self.write_keyword("CAST");
1726                        self.write("(");
1727                        self.generate_expression(&f.this)?;
1728                        self.write_keyword(" AS ");
1729                        self.write_keyword("JSON");
1730                        self.write(")");
1731                        return Ok(());
1732                    }
1733                    Some(DialectType::Hive) | Some(DialectType::Spark)
1734                    | Some(DialectType::MySQL)
1735                    | Some(DialectType::SingleStore) | Some(DialectType::TiDB)
1736                    | Some(DialectType::TSQL) => {
1737                        // Hive/Spark/MySQL/TSQL: just emit the string literal
1738                        self.generate_expression(&f.this)?;
1739                        return Ok(());
1740                    }
1741                    Some(DialectType::DuckDB) => "JSON",
1742                    _ => "PARSE_JSON",
1743                };
1744                self.generate_simple_func(name, &f.this)
1745            }
1746            Expression::ToJson(f) => self.generate_simple_func("TO_JSON", &f.this),
1747            Expression::JsonSet(f) => self.generate_json_modify("JSON_SET", f),
1748            Expression::JsonInsert(f) => self.generate_json_modify("JSON_INSERT", f),
1749            Expression::JsonRemove(f) => self.generate_json_path("JSON_REMOVE", f),
1750            Expression::JsonMergePatch(f) => self.generate_binary_func("JSON_MERGE_PATCH", &f.this, &f.expression),
1751            Expression::JsonArrayAgg(f) => self.generate_json_array_agg(f),
1752            Expression::JsonObjectAgg(f) => self.generate_json_object_agg(f),
1753
1754            // Type casting/conversion
1755            Expression::Convert(f) => self.generate_convert(f),
1756            Expression::Typeof(f) => self.generate_simple_func("TYPEOF", &f.this),
1757
1758            // Additional expressions
1759            Expression::Lambda(f) => self.generate_lambda(f),
1760            Expression::Parameter(f) => self.generate_parameter(f),
1761            Expression::Placeholder(f) => self.generate_placeholder(f),
1762            Expression::NamedArgument(f) => self.generate_named_argument(f),
1763            Expression::TableArgument(f) => self.generate_table_argument(f),
1764            Expression::SqlComment(f) => self.generate_sql_comment(f),
1765
1766            // Additional predicates
1767            Expression::NullSafeEq(op) => self.generate_null_safe_eq(op),
1768            Expression::NullSafeNeq(op) => self.generate_null_safe_neq(op),
1769            Expression::Glob(op) => self.generate_binary_op(op, "GLOB"),
1770            Expression::SimilarTo(f) => self.generate_similar_to(f),
1771            Expression::Any(f) => self.generate_quantified("ANY", f),
1772            Expression::All(f) => self.generate_quantified("ALL", f),
1773            Expression::Overlaps(f) => self.generate_overlaps(f),
1774
1775            // Bitwise operations
1776            Expression::BitwiseLeftShift(op) => {
1777                if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino)) {
1778                    self.write_keyword("BITWISE_ARITHMETIC_SHIFT_LEFT");
1779                    self.write("(");
1780                    self.generate_expression(&op.left)?;
1781                    self.write(", ");
1782                    self.generate_expression(&op.right)?;
1783                    self.write(")");
1784                    Ok(())
1785                } else if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks)) {
1786                    self.write_keyword("SHIFTLEFT");
1787                    self.write("(");
1788                    self.generate_expression(&op.left)?;
1789                    self.write(", ");
1790                    self.generate_expression(&op.right)?;
1791                    self.write(")");
1792                    Ok(())
1793                } else {
1794                    self.generate_binary_op(op, "<<")
1795                }
1796            }
1797            Expression::BitwiseRightShift(op) => {
1798                if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino)) {
1799                    self.write_keyword("BITWISE_ARITHMETIC_SHIFT_RIGHT");
1800                    self.write("(");
1801                    self.generate_expression(&op.left)?;
1802                    self.write(", ");
1803                    self.generate_expression(&op.right)?;
1804                    self.write(")");
1805                    Ok(())
1806                } else if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks)) {
1807                    self.write_keyword("SHIFTRIGHT");
1808                    self.write("(");
1809                    self.generate_expression(&op.left)?;
1810                    self.write(", ");
1811                    self.generate_expression(&op.right)?;
1812                    self.write(")");
1813                    Ok(())
1814                } else {
1815                    self.generate_binary_op(op, ">>")
1816                }
1817            }
1818            Expression::BitwiseAndAgg(f) => self.generate_agg_func("BIT_AND", f),
1819            Expression::BitwiseOrAgg(f) => self.generate_agg_func("BIT_OR", f),
1820            Expression::BitwiseXorAgg(f) => self.generate_agg_func("BIT_XOR", f),
1821
1822            // Array/struct/map access
1823            Expression::Subscript(s) => self.generate_subscript(s),
1824            Expression::Dot(d) => self.generate_dot_access(d),
1825            Expression::MethodCall(m) => self.generate_method_call(m),
1826            Expression::ArraySlice(s) => self.generate_array_slice(s),
1827
1828            Expression::And(op) => self.generate_binary_op(op, "AND"),
1829            Expression::Or(op) => self.generate_binary_op(op, "OR"),
1830            Expression::Add(op) => self.generate_binary_op(op, "+"),
1831            Expression::Sub(op) => self.generate_binary_op(op, "-"),
1832            Expression::Mul(op) => self.generate_binary_op(op, "*"),
1833            Expression::Div(op) => self.generate_binary_op(op, "/"),
1834            Expression::IntDiv(f) => {
1835                use crate::dialects::DialectType;
1836                if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
1837                    // DuckDB uses // operator for integer division
1838                    self.generate_expression(&f.this)?;
1839                    self.write(" // ");
1840                    self.generate_expression(&f.expression)?;
1841                    Ok(())
1842                } else if matches!(self.config.dialect, Some(DialectType::Hive | DialectType::Spark | DialectType::Databricks)) {
1843                    // Hive/Spark use DIV as an infix operator
1844                    self.generate_expression(&f.this)?;
1845                    self.write(" ");
1846                    self.write_keyword("DIV");
1847                    self.write(" ");
1848                    self.generate_expression(&f.expression)?;
1849                    Ok(())
1850                } else {
1851                    // Other dialects use DIV function
1852                    self.write_keyword("DIV");
1853                    self.write("(");
1854                    self.generate_expression(&f.this)?;
1855                    self.write(", ");
1856                    self.generate_expression(&f.expression)?;
1857                    self.write(")");
1858                    Ok(())
1859                }
1860            }
1861            Expression::Mod(op) => {
1862                if matches!(self.config.dialect, Some(DialectType::Teradata)) {
1863                    self.generate_binary_op(op, "MOD")
1864                } else {
1865                    self.generate_binary_op(op, "%")
1866                }
1867            }
1868            Expression::Eq(op) => self.generate_binary_op(op, "="),
1869            Expression::Neq(op) => self.generate_binary_op(op, "<>"),
1870            Expression::Lt(op) => self.generate_binary_op(op, "<"),
1871            Expression::Lte(op) => self.generate_binary_op(op, "<="),
1872            Expression::Gt(op) => self.generate_binary_op(op, ">"),
1873            Expression::Gte(op) => self.generate_binary_op(op, ">="),
1874            Expression::Like(op) => self.generate_like_op(op, "LIKE"),
1875            Expression::ILike(op) => self.generate_like_op(op, "ILIKE"),
1876            Expression::Match(op) => self.generate_binary_op(op, "MATCH"),
1877            Expression::Concat(op) => {
1878                // In Solr, || is OR, not string concatenation (DPIPE_IS_STRING_CONCAT = False)
1879                if self.config.dialect == Some(DialectType::Solr) {
1880                    self.generate_binary_op(op, "OR")
1881                } else {
1882                    self.generate_binary_op(op, "||")
1883                }
1884            }
1885            Expression::BitwiseAnd(op) => {
1886                // Presto/Trino use BITWISE_AND function
1887                if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino)) {
1888                    self.write_keyword("BITWISE_AND");
1889                    self.write("(");
1890                    self.generate_expression(&op.left)?;
1891                    self.write(", ");
1892                    self.generate_expression(&op.right)?;
1893                    self.write(")");
1894                    Ok(())
1895                } else {
1896                    self.generate_binary_op(op, "&")
1897                }
1898            }
1899            Expression::BitwiseOr(op) => {
1900                // Presto/Trino use BITWISE_OR function
1901                if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino)) {
1902                    self.write_keyword("BITWISE_OR");
1903                    self.write("(");
1904                    self.generate_expression(&op.left)?;
1905                    self.write(", ");
1906                    self.generate_expression(&op.right)?;
1907                    self.write(")");
1908                    Ok(())
1909                } else {
1910                    self.generate_binary_op(op, "|")
1911                }
1912            }
1913            Expression::BitwiseXor(op) => {
1914                // Presto/Trino use BITWISE_XOR function, PostgreSQL uses #, others use ^
1915                if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino)) {
1916                    self.write_keyword("BITWISE_XOR");
1917                    self.write("(");
1918                    self.generate_expression(&op.left)?;
1919                    self.write(", ");
1920                    self.generate_expression(&op.right)?;
1921                    self.write(")");
1922                    Ok(())
1923                } else if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) {
1924                    self.generate_binary_op(op, "#")
1925                } else {
1926                    self.generate_binary_op(op, "^")
1927                }
1928            }
1929            Expression::Adjacent(op) => self.generate_binary_op(op, "-|-"),
1930            Expression::TsMatch(op) => self.generate_binary_op(op, "@@"),
1931            Expression::PropertyEQ(op) => self.generate_binary_op(op, ":="),
1932            Expression::ArrayContainsAll(op) => self.generate_binary_op(op, "@>"),
1933            Expression::ArrayContainedBy(op) => self.generate_binary_op(op, "<@"),
1934            Expression::ArrayOverlaps(op) => self.generate_binary_op(op, "&&"),
1935            Expression::JSONBContainsAllTopKeys(op) => self.generate_binary_op(op, "?&"),
1936            Expression::JSONBContainsAnyTopKeys(op) => self.generate_binary_op(op, "?|"),
1937            Expression::JSONBContains(f) => {
1938                // PostgreSQL JSONB contains key operator: a ? b
1939                self.generate_expression(&f.this)?;
1940                self.write_space();
1941                self.write("?");
1942                self.write_space();
1943                self.generate_expression(&f.expression)
1944            }
1945            Expression::JSONBDeleteAtPath(op) => self.generate_binary_op(op, "#-"),
1946            Expression::ExtendsLeft(op) => self.generate_binary_op(op, "&<"),
1947            Expression::ExtendsRight(op) => self.generate_binary_op(op, "&>"),
1948            Expression::Not(op) => self.generate_unary_op(op, "NOT"),
1949            Expression::Neg(op) => self.generate_unary_op(op, "-"),
1950            Expression::BitwiseNot(op) => {
1951                // Presto/Trino use BITWISE_NOT function
1952                if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino)) {
1953                    self.write_keyword("BITWISE_NOT");
1954                    self.write("(");
1955                    self.generate_expression(&op.this)?;
1956                    self.write(")");
1957                    Ok(())
1958                } else {
1959                    self.generate_unary_op(op, "~")
1960                }
1961            }
1962            Expression::In(in_expr) => self.generate_in(in_expr),
1963            Expression::Between(between) => self.generate_between(between),
1964            Expression::IsNull(is_null) => self.generate_is_null(is_null),
1965            Expression::IsTrue(is_true) => self.generate_is_true(is_true),
1966            Expression::IsFalse(is_false) => self.generate_is_false(is_false),
1967            Expression::IsJson(is_json) => self.generate_is_json(is_json),
1968            Expression::Is(is_expr) => self.generate_is(is_expr),
1969            Expression::Exists(exists) => self.generate_exists(exists),
1970            Expression::MemberOf(member_of) => self.generate_member_of(member_of),
1971            Expression::Subquery(subquery) => self.generate_subquery(subquery),
1972            Expression::Paren(paren) => {
1973                // JoinedTable already outputs its own parentheses, so don't double-wrap
1974                let skip_parens = matches!(&paren.this, Expression::JoinedTable(_));
1975
1976                // Check if inner expression is a statement (SELECT, UNION, etc.) for pretty formatting
1977                let is_statement = matches!(
1978                    &paren.this,
1979                    Expression::Select(_) | Expression::Union(_) |
1980                    Expression::Intersect(_) | Expression::Except(_) |
1981                    Expression::Subquery(_)
1982                );
1983
1984                if !skip_parens {
1985                    self.write("(");
1986                    if self.config.pretty && is_statement {
1987                        self.write_newline();
1988                        self.indent_level += 1;
1989                        self.write_indent();
1990                    }
1991                }
1992                self.generate_expression(&paren.this)?;
1993                if !skip_parens {
1994                    if self.config.pretty && is_statement {
1995                        self.write_newline();
1996                        self.indent_level -= 1;
1997                    }
1998                    self.write(")");
1999                }
2000                // Output trailing comments after closing paren
2001                for comment in &paren.trailing_comments {
2002                    self.write(" ");
2003                    self.write(comment);
2004                }
2005                Ok(())
2006            }
2007            Expression::Array(arr) => self.generate_array(arr),
2008            Expression::Tuple(tuple) => self.generate_tuple(tuple),
2009            Expression::PipeOperator(pipe) => self.generate_pipe_operator(pipe),
2010            Expression::Ordered(ordered) => self.generate_ordered(ordered),
2011            Expression::DataType(dt) => self.generate_data_type(dt),
2012            Expression::Raw(raw) => {
2013                self.write(&raw.sql);
2014                Ok(())
2015            }
2016            Expression::Command(cmd) => {
2017                self.write(&cmd.this);
2018                Ok(())
2019            }
2020            Expression::Kill(kill) => {
2021                self.write_keyword("KILL");
2022                if let Some(kind) = &kill.kind {
2023                    self.write_space();
2024                    self.write_keyword(kind);
2025                }
2026                self.write_space();
2027                self.generate_expression(&kill.this)?;
2028                Ok(())
2029            }
2030            Expression::Execute(exec) => {
2031                self.write_keyword("EXEC");
2032                self.write_space();
2033                self.generate_expression(&exec.this)?;
2034                for (i, param) in exec.parameters.iter().enumerate() {
2035                    if i == 0 {
2036                        self.write_space();
2037                    } else {
2038                        self.write(", ");
2039                    }
2040                    self.write(&param.name);
2041                    self.write("=");
2042                    self.generate_expression(&param.value)?;
2043                }
2044                Ok(())
2045            }
2046            Expression::Annotated(annotated) => {
2047                self.generate_expression(&annotated.this)?;
2048                for comment in &annotated.trailing_comments {
2049                    self.write(" ");
2050                    self.write_formatted_comment(comment);
2051                }
2052                Ok(())
2053            }
2054
2055            // DDL statements
2056            Expression::CreateTable(ct) => self.generate_create_table(ct),
2057            Expression::DropTable(dt) => self.generate_drop_table(dt),
2058            Expression::AlterTable(at) => self.generate_alter_table(at),
2059            Expression::CreateIndex(ci) => self.generate_create_index(ci),
2060            Expression::DropIndex(di) => self.generate_drop_index(di),
2061            Expression::CreateView(cv) => self.generate_create_view(cv),
2062            Expression::DropView(dv) => self.generate_drop_view(dv),
2063            Expression::AlterView(av) => self.generate_alter_view(av),
2064            Expression::AlterIndex(ai) => self.generate_alter_index(ai),
2065            Expression::Truncate(tr) => self.generate_truncate(tr),
2066            Expression::Use(u) => self.generate_use(u),
2067            // Phase 4: Additional DDL statements
2068            Expression::CreateSchema(cs) => self.generate_create_schema(cs),
2069            Expression::DropSchema(ds) => self.generate_drop_schema(ds),
2070            Expression::DropNamespace(dn) => self.generate_drop_namespace(dn),
2071            Expression::CreateDatabase(cd) => self.generate_create_database(cd),
2072            Expression::DropDatabase(dd) => self.generate_drop_database(dd),
2073            Expression::CreateFunction(cf) => self.generate_create_function(cf),
2074            Expression::DropFunction(df) => self.generate_drop_function(df),
2075            Expression::CreateProcedure(cp) => self.generate_create_procedure(cp),
2076            Expression::DropProcedure(dp) => self.generate_drop_procedure(dp),
2077            Expression::CreateSequence(cs) => self.generate_create_sequence(cs),
2078            Expression::DropSequence(ds) => self.generate_drop_sequence(ds),
2079            Expression::AlterSequence(als) => self.generate_alter_sequence(als),
2080            Expression::CreateTrigger(ct) => self.generate_create_trigger(ct),
2081            Expression::DropTrigger(dt) => self.generate_drop_trigger(dt),
2082            Expression::CreateType(ct) => self.generate_create_type(ct),
2083            Expression::DropType(dt) => self.generate_drop_type(dt),
2084            Expression::Describe(d) => self.generate_describe(d),
2085            Expression::Show(s) => self.generate_show(s),
2086
2087            // CACHE/UNCACHE/LOAD TABLE (Spark/Hive)
2088            Expression::Cache(c) => self.generate_cache(c),
2089            Expression::Uncache(u) => self.generate_uncache(u),
2090            Expression::LoadData(l) => self.generate_load_data(l),
2091            Expression::Pragma(p) => self.generate_pragma(p),
2092            Expression::Grant(g) => self.generate_grant(g),
2093            Expression::Revoke(r) => self.generate_revoke(r),
2094            Expression::Comment(c) => self.generate_comment(c),
2095            Expression::SetStatement(s) => self.generate_set_statement(s),
2096
2097            // PIVOT/UNPIVOT
2098            Expression::Pivot(pivot) => self.generate_pivot(pivot),
2099            Expression::Unpivot(unpivot) => self.generate_unpivot(unpivot),
2100
2101            // VALUES table constructor
2102            Expression::Values(values) => self.generate_values(values),
2103
2104
2105            // === BATCH-GENERATED MATCH ARMS (481 variants) ===
2106            Expression::AIAgg(e) => self.generate_ai_agg(e),
2107            Expression::AIClassify(e) => self.generate_ai_classify(e),
2108            Expression::AddPartition(e) => self.generate_add_partition(e),
2109            Expression::AlgorithmProperty(e) => self.generate_algorithm_property(e),
2110            Expression::Aliases(e) => self.generate_aliases(e),
2111            Expression::AllowedValuesProperty(e) => self.generate_allowed_values_property(e),
2112            Expression::AlterColumn(e) => self.generate_alter_column(e),
2113            Expression::AlterSession(e) => self.generate_alter_session(e),
2114            Expression::AlterSet(e) => self.generate_alter_set(e),
2115            Expression::AlterSortKey(e) => self.generate_alter_sort_key(e),
2116            Expression::Analyze(e) => self.generate_analyze(e),
2117            Expression::AnalyzeDelete(e) => self.generate_analyze_delete(e),
2118            Expression::AnalyzeHistogram(e) => self.generate_analyze_histogram(e),
2119            Expression::AnalyzeListChainedRows(e) => self.generate_analyze_list_chained_rows(e),
2120            Expression::AnalyzeSample(e) => self.generate_analyze_sample(e),
2121            Expression::AnalyzeStatistics(e) => self.generate_analyze_statistics(e),
2122            Expression::AnalyzeValidate(e) => self.generate_analyze_validate(e),
2123            Expression::AnalyzeWith(e) => self.generate_analyze_with(e),
2124            Expression::Anonymous(e) => self.generate_anonymous(e),
2125            Expression::AnonymousAggFunc(e) => self.generate_anonymous_agg_func(e),
2126            Expression::Apply(e) => self.generate_apply(e),
2127            Expression::ApproxPercentileEstimate(e) => self.generate_approx_percentile_estimate(e),
2128            Expression::ApproxQuantile(e) => self.generate_approx_quantile(e),
2129            Expression::ApproxQuantiles(e) => self.generate_approx_quantiles(e),
2130            Expression::ApproxTopK(e) => self.generate_approx_top_k(e),
2131            Expression::ApproxTopKAccumulate(e) => self.generate_approx_top_k_accumulate(e),
2132            Expression::ApproxTopKCombine(e) => self.generate_approx_top_k_combine(e),
2133            Expression::ApproxTopKEstimate(e) => self.generate_approx_top_k_estimate(e),
2134            Expression::ApproxTopSum(e) => self.generate_approx_top_sum(e),
2135            Expression::ArgMax(e) => self.generate_arg_max(e),
2136            Expression::ArgMin(e) => self.generate_arg_min(e),
2137            Expression::ArrayAll(e) => self.generate_array_all(e),
2138            Expression::ArrayAny(e) => self.generate_array_any(e),
2139            Expression::ArrayConstructCompact(e) => self.generate_array_construct_compact(e),
2140            Expression::ArraySum(e) => self.generate_array_sum(e),
2141            Expression::AtIndex(e) => self.generate_at_index(e),
2142            Expression::Attach(e) => self.generate_attach(e),
2143            Expression::AttachOption(e) => self.generate_attach_option(e),
2144            Expression::AutoIncrementProperty(e) => self.generate_auto_increment_property(e),
2145            Expression::AutoRefreshProperty(e) => self.generate_auto_refresh_property(e),
2146            Expression::BackupProperty(e) => self.generate_backup_property(e),
2147            Expression::Base64DecodeBinary(e) => self.generate_base64_decode_binary(e),
2148            Expression::Base64DecodeString(e) => self.generate_base64_decode_string(e),
2149            Expression::Base64Encode(e) => self.generate_base64_encode(e),
2150            Expression::BlockCompressionProperty(e) => self.generate_block_compression_property(e),
2151            Expression::Booland(e) => self.generate_booland(e),
2152            Expression::Boolor(e) => self.generate_boolor(e),
2153            Expression::BuildProperty(e) => self.generate_build_property(e),
2154            Expression::ByteString(e) => self.generate_byte_string(e),
2155            Expression::CaseSpecificColumnConstraint(e) => self.generate_case_specific_column_constraint(e),
2156            Expression::CastToStrType(e) => self.generate_cast_to_str_type(e),
2157            Expression::Changes(e) => self.generate_changes(e),
2158            Expression::CharacterSetColumnConstraint(e) => self.generate_character_set_column_constraint(e),
2159            Expression::CharacterSetProperty(e) => self.generate_character_set_property(e),
2160            Expression::CheckColumnConstraint(e) => self.generate_check_column_constraint(e),
2161            Expression::CheckJson(e) => self.generate_check_json(e),
2162            Expression::CheckXml(e) => self.generate_check_xml(e),
2163            Expression::ChecksumProperty(e) => self.generate_checksum_property(e),
2164            Expression::Clone(e) => self.generate_clone(e),
2165            Expression::ClusterBy(e) => self.generate_cluster_by(e),
2166            Expression::ClusteredByProperty(e) => self.generate_clustered_by_property(e),
2167            Expression::CollateProperty(e) => self.generate_collate_property(e),
2168            Expression::ColumnConstraint(e) => self.generate_column_constraint(e),
2169            Expression::ColumnDef(e) => self.generate_column_def_expr(e),
2170            Expression::ColumnPosition(e) => self.generate_column_position(e),
2171            Expression::ColumnPrefix(e) => self.generate_column_prefix(e),
2172            Expression::Columns(e) => self.generate_columns(e),
2173            Expression::CombinedAggFunc(e) => self.generate_combined_agg_func(e),
2174            Expression::CombinedParameterizedAgg(e) => self.generate_combined_parameterized_agg(e),
2175            Expression::Commit(e) => self.generate_commit(e),
2176            Expression::Comprehension(e) => self.generate_comprehension(e),
2177            Expression::Compress(e) => self.generate_compress(e),
2178            Expression::CompressColumnConstraint(e) => self.generate_compress_column_constraint(e),
2179            Expression::ComputedColumnConstraint(e) => self.generate_computed_column_constraint(e),
2180            Expression::ConditionalInsert(e) => self.generate_conditional_insert(e),
2181            Expression::Constraint(e) => self.generate_constraint(e),
2182            Expression::ConvertTimezone(e) => self.generate_convert_timezone(e),
2183            Expression::ConvertToCharset(e) => self.generate_convert_to_charset(e),
2184            Expression::Copy(e) => self.generate_copy(e),
2185            Expression::CopyParameter(e) => self.generate_copy_parameter(e),
2186            Expression::Corr(e) => self.generate_corr(e),
2187            Expression::CosineDistance(e) => self.generate_cosine_distance(e),
2188            Expression::CovarPop(e) => self.generate_covar_pop(e),
2189            Expression::CovarSamp(e) => self.generate_covar_samp(e),
2190            Expression::Credentials(e) => self.generate_credentials(e),
2191            Expression::CredentialsProperty(e) => self.generate_credentials_property(e),
2192            Expression::Cte(e) => self.generate_cte(e),
2193            Expression::Cube(e) => self.generate_cube(e),
2194            Expression::CurrentDatetime(e) => self.generate_current_datetime(e),
2195            Expression::CurrentSchema(e) => self.generate_current_schema(e),
2196            Expression::CurrentSchemas(e) => self.generate_current_schemas(e),
2197            Expression::CurrentUser(e) => self.generate_current_user(e),
2198            Expression::DPipe(e) => self.generate_d_pipe(e),
2199            Expression::DataBlocksizeProperty(e) => self.generate_data_blocksize_property(e),
2200            Expression::DataDeletionProperty(e) => self.generate_data_deletion_property(e),
2201            Expression::Date(e) => self.generate_date_func(e),
2202            Expression::DateBin(e) => self.generate_date_bin(e),
2203            Expression::DateFormatColumnConstraint(e) => self.generate_date_format_column_constraint(e),
2204            Expression::DateFromParts(e) => self.generate_date_from_parts(e),
2205            Expression::Datetime(e) => self.generate_datetime(e),
2206            Expression::DatetimeAdd(e) => self.generate_datetime_add(e),
2207            Expression::DatetimeDiff(e) => self.generate_datetime_diff(e),
2208            Expression::DatetimeSub(e) => self.generate_datetime_sub(e),
2209            Expression::DatetimeTrunc(e) => self.generate_datetime_trunc(e),
2210            Expression::Dayname(e) => self.generate_dayname(e),
2211            Expression::Declare(e) => self.generate_declare(e),
2212            Expression::DeclareItem(e) => self.generate_declare_item(e),
2213            Expression::DecodeCase(e) => self.generate_decode_case(e),
2214            Expression::DecompressBinary(e) => self.generate_decompress_binary(e),
2215            Expression::DecompressString(e) => self.generate_decompress_string(e),
2216            Expression::Decrypt(e) => self.generate_decrypt(e),
2217            Expression::DecryptRaw(e) => self.generate_decrypt_raw(e),
2218            Expression::DefinerProperty(e) => self.generate_definer_property(e),
2219            Expression::Detach(e) => self.generate_detach(e),
2220            Expression::DictProperty(e) => self.generate_dict_property(e),
2221            Expression::DictRange(e) => self.generate_dict_range(e),
2222            Expression::Directory(e) => self.generate_directory(e),
2223            Expression::DistKeyProperty(e) => self.generate_dist_key_property(e),
2224            Expression::DistStyleProperty(e) => self.generate_dist_style_property(e),
2225            Expression::DistributeBy(e) => self.generate_distribute_by(e),
2226            Expression::DistributedByProperty(e) => self.generate_distributed_by_property(e),
2227            Expression::DotProduct(e) => self.generate_dot_product(e),
2228            Expression::DropPartition(e) => self.generate_drop_partition(e),
2229            Expression::DuplicateKeyProperty(e) => self.generate_duplicate_key_property(e),
2230            Expression::Elt(e) => self.generate_elt(e),
2231            Expression::Encode(e) => self.generate_encode(e),
2232            Expression::EncodeProperty(e) => self.generate_encode_property(e),
2233            Expression::Encrypt(e) => self.generate_encrypt(e),
2234            Expression::EncryptRaw(e) => self.generate_encrypt_raw(e),
2235            Expression::EngineProperty(e) => self.generate_engine_property(e),
2236            Expression::EnviromentProperty(e) => self.generate_enviroment_property(e),
2237            Expression::EphemeralColumnConstraint(e) => self.generate_ephemeral_column_constraint(e),
2238            Expression::EqualNull(e) => self.generate_equal_null(e),
2239            Expression::EuclideanDistance(e) => self.generate_euclidean_distance(e),
2240            Expression::ExecuteAsProperty(e) => self.generate_execute_as_property(e),
2241            Expression::Export(e) => self.generate_export(e),
2242            Expression::ExternalProperty(e) => self.generate_external_property(e),
2243            Expression::FallbackProperty(e) => self.generate_fallback_property(e),
2244            Expression::FarmFingerprint(e) => self.generate_farm_fingerprint(e),
2245            Expression::FeaturesAtTime(e) => self.generate_features_at_time(e),
2246            Expression::Fetch(e) => self.generate_fetch(e),
2247            Expression::FileFormatProperty(e) => self.generate_file_format_property(e),
2248            Expression::Filter(e) => self.generate_filter(e),
2249            Expression::Float64(e) => self.generate_float64(e),
2250            Expression::ForIn(e) => self.generate_for_in(e),
2251            Expression::ForeignKey(e) => self.generate_foreign_key(e),
2252            Expression::Format(e) => self.generate_format(e),
2253            Expression::FormatPhrase(e) => self.generate_format_phrase(e),
2254            Expression::FreespaceProperty(e) => self.generate_freespace_property(e),
2255            Expression::From(e) => self.generate_from(e),
2256            Expression::FromBase(e) => self.generate_from_base(e),
2257            Expression::FromTimeZone(e) => self.generate_from_time_zone(e),
2258            Expression::GapFill(e) => self.generate_gap_fill(e),
2259            Expression::GenerateDateArray(e) => self.generate_generate_date_array(e),
2260            Expression::GenerateEmbedding(e) => self.generate_generate_embedding(e),
2261            Expression::GenerateSeries(e) => self.generate_generate_series(e),
2262            Expression::GenerateTimestampArray(e) => self.generate_generate_timestamp_array(e),
2263            Expression::GeneratedAsIdentityColumnConstraint(e) => self.generate_generated_as_identity_column_constraint(e),
2264            Expression::GeneratedAsRowColumnConstraint(e) => self.generate_generated_as_row_column_constraint(e),
2265            Expression::Get(e) => self.generate_get(e),
2266            Expression::GetExtract(e) => self.generate_get_extract(e),
2267            Expression::Getbit(e) => self.generate_getbit(e),
2268            Expression::GrantPrincipal(e) => self.generate_grant_principal(e),
2269            Expression::GrantPrivilege(e) => self.generate_grant_privilege(e),
2270            Expression::Group(e) => self.generate_group(e),
2271            Expression::GroupBy(e) => self.generate_group_by(e),
2272            Expression::Grouping(e) => self.generate_grouping(e),
2273            Expression::GroupingId(e) => self.generate_grouping_id(e),
2274            Expression::GroupingSets(e) => self.generate_grouping_sets(e),
2275            Expression::HashAgg(e) => self.generate_hash_agg(e),
2276            Expression::Having(e) => self.generate_having(e),
2277            Expression::HavingMax(e) => self.generate_having_max(e),
2278            Expression::Heredoc(e) => self.generate_heredoc(e),
2279            Expression::HexEncode(e) => self.generate_hex_encode(e),
2280            Expression::Hll(e) => self.generate_hll(e),
2281            Expression::InOutColumnConstraint(e) => self.generate_in_out_column_constraint(e),
2282            Expression::IncludeProperty(e) => self.generate_include_property(e),
2283            Expression::Index(e) => self.generate_index(e),
2284            Expression::IndexColumnConstraint(e) => self.generate_index_column_constraint(e),
2285            Expression::IndexConstraintOption(e) => self.generate_index_constraint_option(e),
2286            Expression::IndexParameters(e) => self.generate_index_parameters(e),
2287            Expression::IndexTableHint(e) => self.generate_index_table_hint(e),
2288            Expression::InheritsProperty(e) => self.generate_inherits_property(e),
2289            Expression::InputModelProperty(e) => self.generate_input_model_property(e),
2290            Expression::InputOutputFormat(e) => self.generate_input_output_format(e),
2291            Expression::Install(e) => self.generate_install(e),
2292            Expression::IntervalOp(e) => self.generate_interval_op(e),
2293            Expression::IntervalSpan(e) => self.generate_interval_span(e),
2294            Expression::IntoClause(e) => self.generate_into_clause(e),
2295            Expression::Introducer(e) => self.generate_introducer(e),
2296            Expression::IsolatedLoadingProperty(e) => self.generate_isolated_loading_property(e),
2297            Expression::JSON(e) => self.generate_json(e),
2298            Expression::JSONArray(e) => self.generate_json_array(e),
2299            Expression::JSONArrayAgg(e) => self.generate_json_array_agg_struct(e),
2300            Expression::JSONArrayAppend(e) => self.generate_json_array_append(e),
2301            Expression::JSONArrayContains(e) => self.generate_json_array_contains(e),
2302            Expression::JSONArrayInsert(e) => self.generate_json_array_insert(e),
2303            Expression::JSONBExists(e) => self.generate_jsonb_exists(e),
2304            Expression::JSONBExtractScalar(e) => self.generate_jsonb_extract_scalar(e),
2305            Expression::JSONBObjectAgg(e) => self.generate_jsonb_object_agg(e),
2306            Expression::JSONObjectAgg(e) => self.generate_json_object_agg_struct(e),
2307            Expression::JSONColumnDef(e) => self.generate_json_column_def(e),
2308            Expression::JSONExists(e) => self.generate_json_exists(e),
2309            Expression::JSONCast(e) => self.generate_json_cast(e),
2310            Expression::JSONExtract(e) => self.generate_json_extract_path(e),
2311            Expression::JSONExtractArray(e) => self.generate_json_extract_array(e),
2312            Expression::JSONExtractQuote(e) => self.generate_json_extract_quote(e),
2313            Expression::JSONExtractScalar(e) => self.generate_json_extract_scalar(e),
2314            Expression::JSONFormat(e) => self.generate_json_format(e),
2315            Expression::JSONKeyValue(e) => self.generate_json_key_value(e),
2316            Expression::JSONKeys(e) => self.generate_json_keys(e),
2317            Expression::JSONKeysAtDepth(e) => self.generate_json_keys_at_depth(e),
2318            Expression::JSONPath(e) => self.generate_json_path_expr(e),
2319            Expression::JSONPathFilter(e) => self.generate_json_path_filter(e),
2320            Expression::JSONPathKey(e) => self.generate_json_path_key(e),
2321            Expression::JSONPathRecursive(e) => self.generate_json_path_recursive(e),
2322            Expression::JSONPathRoot(_) => self.generate_json_path_root(),
2323            Expression::JSONPathScript(e) => self.generate_json_path_script(e),
2324            Expression::JSONPathSelector(e) => self.generate_json_path_selector(e),
2325            Expression::JSONPathSlice(e) => self.generate_json_path_slice(e),
2326            Expression::JSONPathSubscript(e) => self.generate_json_path_subscript(e),
2327            Expression::JSONPathUnion(e) => self.generate_json_path_union(e),
2328            Expression::JSONRemove(e) => self.generate_json_remove(e),
2329            Expression::JSONSchema(e) => self.generate_json_schema(e),
2330            Expression::JSONSet(e) => self.generate_json_set(e),
2331            Expression::JSONStripNulls(e) => self.generate_json_strip_nulls(e),
2332            Expression::JSONTable(e) => self.generate_json_table(e),
2333            Expression::JSONType(e) => self.generate_json_type(e),
2334            Expression::JSONValue(e) => self.generate_json_value(e),
2335            Expression::JSONValueArray(e) => self.generate_json_value_array(e),
2336            Expression::JarowinklerSimilarity(e) => self.generate_jarowinkler_similarity(e),
2337            Expression::JoinHint(e) => self.generate_join_hint(e),
2338            Expression::JournalProperty(e) => self.generate_journal_property(e),
2339            Expression::LanguageProperty(e) => self.generate_language_property(e),
2340            Expression::Lateral(e) => self.generate_lateral(e),
2341            Expression::LikeProperty(e) => self.generate_like_property(e),
2342            Expression::Limit(e) => self.generate_limit(e),
2343            Expression::LimitOptions(e) => self.generate_limit_options(e),
2344            Expression::List(e) => self.generate_list(e),
2345            Expression::ToMap(e) => self.generate_tomap(e),
2346            Expression::Localtime(e) => self.generate_localtime(e),
2347            Expression::Localtimestamp(e) => self.generate_localtimestamp(e),
2348            Expression::LocationProperty(e) => self.generate_location_property(e),
2349            Expression::Lock(e) => self.generate_lock(e),
2350            Expression::LockProperty(e) => self.generate_lock_property(e),
2351            Expression::LockingProperty(e) => self.generate_locking_property(e),
2352            Expression::LockingStatement(e) => self.generate_locking_statement(e),
2353            Expression::LogProperty(e) => self.generate_log_property(e),
2354            Expression::MD5Digest(e) => self.generate_md5_digest(e),
2355            Expression::MLForecast(e) => self.generate_ml_forecast(e),
2356            Expression::MLTranslate(e) => self.generate_ml_translate(e),
2357            Expression::MakeInterval(e) => self.generate_make_interval(e),
2358            Expression::ManhattanDistance(e) => self.generate_manhattan_distance(e),
2359            Expression::Map(e) => self.generate_map(e),
2360            Expression::MapCat(e) => self.generate_map_cat(e),
2361            Expression::MapDelete(e) => self.generate_map_delete(e),
2362            Expression::MapInsert(e) => self.generate_map_insert(e),
2363            Expression::MapPick(e) => self.generate_map_pick(e),
2364            Expression::MaskingPolicyColumnConstraint(e) => self.generate_masking_policy_column_constraint(e),
2365            Expression::MatchAgainst(e) => self.generate_match_against(e),
2366            Expression::MatchRecognizeMeasure(e) => self.generate_match_recognize_measure(e),
2367            Expression::MaterializedProperty(e) => self.generate_materialized_property(e),
2368            Expression::Merge(e) => self.generate_merge(e),
2369            Expression::MergeBlockRatioProperty(e) => self.generate_merge_block_ratio_property(e),
2370            Expression::MergeTreeTTL(e) => self.generate_merge_tree_ttl(e),
2371            Expression::MergeTreeTTLAction(e) => self.generate_merge_tree_ttl_action(e),
2372            Expression::Minhash(e) => self.generate_minhash(e),
2373            Expression::ModelAttribute(e) => self.generate_model_attribute(e),
2374            Expression::Monthname(e) => self.generate_monthname(e),
2375            Expression::MultitableInserts(e) => self.generate_multitable_inserts(e),
2376            Expression::NextValueFor(e) => self.generate_next_value_for(e),
2377            Expression::Normal(e) => self.generate_normal(e),
2378            Expression::Normalize(e) => self.generate_normalize(e),
2379            Expression::NotNullColumnConstraint(e) => self.generate_not_null_column_constraint(e),
2380            Expression::Nullif(e) => self.generate_nullif(e),
2381            Expression::NumberToStr(e) => self.generate_number_to_str(e),
2382            Expression::ObjectAgg(e) => self.generate_object_agg(e),
2383            Expression::ObjectIdentifier(e) => self.generate_object_identifier(e),
2384            Expression::ObjectInsert(e) => self.generate_object_insert(e),
2385            Expression::Offset(e) => self.generate_offset(e),
2386            Expression::Qualify(e) => self.generate_qualify(e),
2387            Expression::OnCluster(e) => self.generate_on_cluster(e),
2388            Expression::OnCommitProperty(e) => self.generate_on_commit_property(e),
2389            Expression::OnCondition(e) => self.generate_on_condition(e),
2390            Expression::OnConflict(e) => self.generate_on_conflict(e),
2391            Expression::OnProperty(e) => self.generate_on_property(e),
2392            Expression::Opclass(e) => self.generate_opclass(e),
2393            Expression::OpenJSON(e) => self.generate_open_json(e),
2394            Expression::OpenJSONColumnDef(e) => self.generate_open_json_column_def(e),
2395            Expression::Operator(e) => self.generate_operator(e),
2396            Expression::OrderBy(e) => self.generate_order_by(e),
2397            Expression::OutputModelProperty(e) => self.generate_output_model_property(e),
2398            Expression::OverflowTruncateBehavior(e) => self.generate_overflow_truncate_behavior(e),
2399            Expression::ParameterizedAgg(e) => self.generate_parameterized_agg(e),
2400            Expression::ParseDatetime(e) => self.generate_parse_datetime(e),
2401            Expression::ParseIp(e) => self.generate_parse_ip(e),
2402            Expression::ParseJSON(e) => self.generate_parse_json(e),
2403            Expression::ParseTime(e) => self.generate_parse_time(e),
2404            Expression::ParseUrl(e) => self.generate_parse_url(e),
2405            Expression::Partition(e) => self.generate_partition_expr(e),
2406            Expression::PartitionBoundSpec(e) => self.generate_partition_bound_spec(e),
2407            Expression::PartitionByListProperty(e) => self.generate_partition_by_list_property(e),
2408            Expression::PartitionByRangeProperty(e) => self.generate_partition_by_range_property(e),
2409            Expression::PartitionByRangePropertyDynamic(e) => self.generate_partition_by_range_property_dynamic(e),
2410            Expression::PartitionByTruncate(e) => self.generate_partition_by_truncate(e),
2411            Expression::PartitionList(e) => self.generate_partition_list(e),
2412            Expression::PartitionRange(e) => self.generate_partition_range(e),
2413            Expression::PartitionedByBucket(e) => self.generate_partitioned_by_bucket(e),
2414            Expression::PartitionedByProperty(e) => self.generate_partitioned_by_property(e),
2415            Expression::PartitionedOfProperty(e) => self.generate_partitioned_of_property(e),
2416            Expression::PeriodForSystemTimeConstraint(e) => self.generate_period_for_system_time_constraint(e),
2417            Expression::PivotAlias(e) => self.generate_pivot_alias(e),
2418            Expression::PivotAny(e) => self.generate_pivot_any(e),
2419            Expression::Predict(e) => self.generate_predict(e),
2420            Expression::PreviousDay(e) => self.generate_previous_day(e),
2421            Expression::PrimaryKey(e) => self.generate_primary_key(e),
2422            Expression::PrimaryKeyColumnConstraint(e) => self.generate_primary_key_column_constraint(e),
2423            Expression::PathColumnConstraint(e) => self.generate_path_column_constraint(e),
2424            Expression::ProjectionDef(e) => self.generate_projection_def(e),
2425            Expression::Properties(e) => self.generate_properties(e),
2426            Expression::Property(e) => self.generate_property(e),
2427            Expression::PseudoType(e) => self.generate_pseudo_type(e),
2428            Expression::Put(e) => self.generate_put(e),
2429            Expression::Quantile(e) => self.generate_quantile(e),
2430            Expression::QueryBand(e) => self.generate_query_band(e),
2431            Expression::QueryOption(e) => self.generate_query_option(e),
2432            Expression::QueryTransform(e) => self.generate_query_transform(e),
2433            Expression::Randn(e) => self.generate_randn(e),
2434            Expression::Randstr(e) => self.generate_randstr(e),
2435            Expression::RangeBucket(e) => self.generate_range_bucket(e),
2436            Expression::RangeN(e) => self.generate_range_n(e),
2437            Expression::ReadCSV(e) => self.generate_read_csv(e),
2438            Expression::ReadParquet(e) => self.generate_read_parquet(e),
2439            Expression::RecursiveWithSearch(e) => self.generate_recursive_with_search(e),
2440            Expression::Reduce(e) => self.generate_reduce(e),
2441            Expression::Reference(e) => self.generate_reference(e),
2442            Expression::Refresh(e) => self.generate_refresh(e),
2443            Expression::RefreshTriggerProperty(e) => self.generate_refresh_trigger_property(e),
2444            Expression::RegexpCount(e) => self.generate_regexp_count(e),
2445            Expression::RegexpExtractAll(e) => self.generate_regexp_extract_all(e),
2446            Expression::RegexpFullMatch(e) => self.generate_regexp_full_match(e),
2447            Expression::RegexpILike(e) => self.generate_regexp_i_like(e),
2448            Expression::RegexpInstr(e) => self.generate_regexp_instr(e),
2449            Expression::RegexpSplit(e) => self.generate_regexp_split(e),
2450            Expression::RegrAvgx(e) => self.generate_regr_avgx(e),
2451            Expression::RegrAvgy(e) => self.generate_regr_avgy(e),
2452            Expression::RegrCount(e) => self.generate_regr_count(e),
2453            Expression::RegrIntercept(e) => self.generate_regr_intercept(e),
2454            Expression::RegrR2(e) => self.generate_regr_r2(e),
2455            Expression::RegrSlope(e) => self.generate_regr_slope(e),
2456            Expression::RegrSxx(e) => self.generate_regr_sxx(e),
2457            Expression::RegrSxy(e) => self.generate_regr_sxy(e),
2458            Expression::RegrSyy(e) => self.generate_regr_syy(e),
2459            Expression::RegrValx(e) => self.generate_regr_valx(e),
2460            Expression::RegrValy(e) => self.generate_regr_valy(e),
2461            Expression::RemoteWithConnectionModelProperty(e) => self.generate_remote_with_connection_model_property(e),
2462            Expression::RenameColumn(e) => self.generate_rename_column(e),
2463            Expression::ReplacePartition(e) => self.generate_replace_partition(e),
2464            Expression::Returning(e) => self.generate_returning(e),
2465            Expression::ReturnsProperty(e) => self.generate_returns_property(e),
2466            Expression::Rollback(e) => self.generate_rollback(e),
2467            Expression::Rollup(e) => self.generate_rollup(e),
2468            Expression::RowFormatDelimitedProperty(e) => self.generate_row_format_delimited_property(e),
2469            Expression::RowFormatProperty(e) => self.generate_row_format_property(e),
2470            Expression::RowFormatSerdeProperty(e) => self.generate_row_format_serde_property(e),
2471            Expression::SHA2(e) => self.generate_sha2(e),
2472            Expression::SHA2Digest(e) => self.generate_sha2_digest(e),
2473            Expression::SafeAdd(e) => self.generate_safe_add(e),
2474            Expression::SafeDivide(e) => self.generate_safe_divide(e),
2475            Expression::SafeMultiply(e) => self.generate_safe_multiply(e),
2476            Expression::SafeSubtract(e) => self.generate_safe_subtract(e),
2477            Expression::SampleProperty(e) => self.generate_sample_property(e),
2478            Expression::Schema(e) => self.generate_schema(e),
2479            Expression::SchemaCommentProperty(e) => self.generate_schema_comment_property(e),
2480            Expression::ScopeResolution(e) => self.generate_scope_resolution(e),
2481            Expression::Search(e) => self.generate_search(e),
2482            Expression::SearchIp(e) => self.generate_search_ip(e),
2483            Expression::SecurityProperty(e) => self.generate_security_property(e),
2484            Expression::SemanticView(e) => self.generate_semantic_view(e),
2485            Expression::SequenceProperties(e) => self.generate_sequence_properties(e),
2486            Expression::SerdeProperties(e) => self.generate_serde_properties(e),
2487            Expression::SessionParameter(e) => self.generate_session_parameter(e),
2488            Expression::Set(e) => self.generate_set(e),
2489            Expression::SetConfigProperty(e) => self.generate_set_config_property(e),
2490            Expression::SetItem(e) => self.generate_set_item(e),
2491            Expression::SetOperation(e) => self.generate_set_operation(e),
2492            Expression::SetProperty(e) => self.generate_set_property(e),
2493            Expression::SettingsProperty(e) => self.generate_settings_property(e),
2494            Expression::SharingProperty(e) => self.generate_sharing_property(e),
2495            Expression::Slice(e) => self.generate_slice(e),
2496            Expression::SortArray(e) => self.generate_sort_array(e),
2497            Expression::SortBy(e) => self.generate_sort_by(e),
2498            Expression::SortKeyProperty(e) => self.generate_sort_key_property(e),
2499            Expression::SplitPart(e) => self.generate_split_part(e),
2500            Expression::SqlReadWriteProperty(e) => self.generate_sql_read_write_property(e),
2501            Expression::SqlSecurityProperty(e) => self.generate_sql_security_property(e),
2502            Expression::StDistance(e) => self.generate_st_distance(e),
2503            Expression::StPoint(e) => self.generate_st_point(e),
2504            Expression::StabilityProperty(e) => self.generate_stability_property(e),
2505            Expression::StandardHash(e) => self.generate_standard_hash(e),
2506            Expression::StorageHandlerProperty(e) => self.generate_storage_handler_property(e),
2507            Expression::StrPosition(e) => self.generate_str_position(e),
2508            Expression::StrToDate(e) => self.generate_str_to_date(e),
2509            Expression::DateStrToDate(f) => self.generate_simple_func("DATE_STR_TO_DATE", &f.this),
2510            Expression::DateToDateStr(f) => self.generate_simple_func("DATE_TO_DATE_STR", &f.this),
2511            Expression::StrToMap(e) => self.generate_str_to_map(e),
2512            Expression::StrToTime(e) => self.generate_str_to_time(e),
2513            Expression::StrToUnix(e) => self.generate_str_to_unix(e),
2514            Expression::StringToArray(e) => self.generate_string_to_array(e),
2515            Expression::Struct(e) => self.generate_struct(e),
2516            Expression::Stuff(e) => self.generate_stuff(e),
2517            Expression::SubstringIndex(e) => self.generate_substring_index(e),
2518            Expression::Summarize(e) => self.generate_summarize(e),
2519            Expression::Systimestamp(e) => self.generate_systimestamp(e),
2520            Expression::TableAlias(e) => self.generate_table_alias(e),
2521            Expression::TableFromRows(e) => self.generate_table_from_rows(e),
2522            Expression::RowsFrom(e) => self.generate_rows_from(e),
2523            Expression::TableSample(e) => self.generate_table_sample(e),
2524            Expression::Tag(e) => self.generate_tag(e),
2525            Expression::Tags(e) => self.generate_tags(e),
2526            Expression::TemporaryProperty(e) => self.generate_temporary_property(e),
2527            Expression::Time(e) => self.generate_time_func(e),
2528            Expression::TimeAdd(e) => self.generate_time_add(e),
2529            Expression::TimeDiff(e) => self.generate_time_diff(e),
2530            Expression::TimeFromParts(e) => self.generate_time_from_parts(e),
2531            Expression::TimeSlice(e) => self.generate_time_slice(e),
2532            Expression::TimeStrToTime(e) => self.generate_time_str_to_time(e),
2533            Expression::TimeSub(e) => self.generate_time_sub(e),
2534            Expression::TimeToStr(e) => self.generate_time_to_str(e),
2535            Expression::TimeTrunc(e) => self.generate_time_trunc(e),
2536            Expression::TimeUnit(e) => self.generate_time_unit(e),
2537            Expression::Timestamp(e) => self.generate_timestamp_func(e),
2538            Expression::TimestampAdd(e) => self.generate_timestamp_add(e),
2539            Expression::TimestampDiff(e) => self.generate_timestamp_diff(e),
2540            Expression::TimestampFromParts(e) => self.generate_timestamp_from_parts(e),
2541            Expression::TimestampSub(e) => self.generate_timestamp_sub(e),
2542            Expression::TimestampTzFromParts(e) => self.generate_timestamp_tz_from_parts(e),
2543            Expression::ToBinary(e) => self.generate_to_binary(e),
2544            Expression::ToBoolean(e) => self.generate_to_boolean(e),
2545            Expression::ToChar(e) => self.generate_to_char(e),
2546            Expression::ToDecfloat(e) => self.generate_to_decfloat(e),
2547            Expression::ToDouble(e) => self.generate_to_double(e),
2548            Expression::ToFile(e) => self.generate_to_file(e),
2549            Expression::ToNumber(e) => self.generate_to_number(e),
2550            Expression::ToTableProperty(e) => self.generate_to_table_property(e),
2551            Expression::Transaction(e) => self.generate_transaction(e),
2552            Expression::Transform(e) => self.generate_transform(e),
2553            Expression::TransformModelProperty(e) => self.generate_transform_model_property(e),
2554            Expression::TransientProperty(e) => self.generate_transient_property(e),
2555            Expression::Translate(e) => self.generate_translate(e),
2556            Expression::TranslateCharacters(e) => self.generate_translate_characters(e),
2557            Expression::TruncateTable(e) => self.generate_truncate_table(e),
2558            Expression::TryBase64DecodeBinary(e) => self.generate_try_base64_decode_binary(e),
2559            Expression::TryBase64DecodeString(e) => self.generate_try_base64_decode_string(e),
2560            Expression::TryToDecfloat(e) => self.generate_try_to_decfloat(e),
2561            Expression::TsOrDsAdd(e) => self.generate_ts_or_ds_add(e),
2562            Expression::TsOrDsDiff(e) => self.generate_ts_or_ds_diff(e),
2563            Expression::TsOrDsToDate(e) => self.generate_ts_or_ds_to_date(e),
2564            Expression::TsOrDsToTime(e) => self.generate_ts_or_ds_to_time(e),
2565            Expression::Unhex(e) => self.generate_unhex(e),
2566            Expression::UnicodeString(e) => self.generate_unicode_string(e),
2567            Expression::Uniform(e) => self.generate_uniform(e),
2568            Expression::UniqueColumnConstraint(e) => self.generate_unique_column_constraint(e),
2569            Expression::UniqueKeyProperty(e) => self.generate_unique_key_property(e),
2570            Expression::RollupProperty(e) => self.generate_rollup_property(e),
2571            Expression::UnixToStr(e) => self.generate_unix_to_str(e),
2572            Expression::UnixToTime(e) => self.generate_unix_to_time(e),
2573            Expression::UnpivotColumns(e) => self.generate_unpivot_columns(e),
2574            Expression::UserDefinedFunction(e) => self.generate_user_defined_function(e),
2575            Expression::UsingTemplateProperty(e) => self.generate_using_template_property(e),
2576            Expression::UtcTime(e) => self.generate_utc_time(e),
2577            Expression::UtcTimestamp(e) => self.generate_utc_timestamp(e),
2578            Expression::Uuid(e) => self.generate_uuid(e),
2579            Expression::Var(v) => {
2580                if matches!(self.config.dialect, Some(DialectType::MySQL))
2581                    && v.this.len() > 2
2582                    && (v.this.starts_with("0x") || v.this.starts_with("0X"))
2583                    && !v.this[2..].chars().all(|c| c.is_ascii_hexdigit())
2584                {
2585                    return self.generate_identifier(&Identifier {
2586                        name: v.this.clone(),
2587                        quoted: true,
2588                        trailing_comments: Vec::new(),
2589                    });
2590                }
2591                self.write(&v.this);
2592                Ok(())
2593            }
2594            Expression::VarMap(e) => self.generate_var_map(e),
2595            Expression::VectorSearch(e) => self.generate_vector_search(e),
2596            Expression::Version(e) => self.generate_version(e),
2597            Expression::ViewAttributeProperty(e) => self.generate_view_attribute_property(e),
2598            Expression::VolatileProperty(e) => self.generate_volatile_property(e),
2599            Expression::WatermarkColumnConstraint(e) => self.generate_watermark_column_constraint(e),
2600            Expression::Week(e) => self.generate_week(e),
2601            Expression::When(e) => self.generate_when(e),
2602            Expression::Whens(e) => self.generate_whens(e),
2603            Expression::Where(e) => self.generate_where(e),
2604            Expression::WidthBucket(e) => self.generate_width_bucket(e),
2605            Expression::Window(e) => self.generate_window(e),
2606            Expression::WindowSpec(e) => self.generate_window_spec(e),
2607            Expression::WithDataProperty(e) => self.generate_with_data_property(e),
2608            Expression::WithFill(e) => self.generate_with_fill(e),
2609            Expression::WithJournalTableProperty(e) => self.generate_with_journal_table_property(e),
2610            Expression::WithOperator(e) => self.generate_with_operator(e),
2611            Expression::WithProcedureOptions(e) => self.generate_with_procedure_options(e),
2612            Expression::WithSchemaBindingProperty(e) => self.generate_with_schema_binding_property(e),
2613            Expression::WithSystemVersioningProperty(e) => self.generate_with_system_versioning_property(e),
2614            Expression::WithTableHint(e) => self.generate_with_table_hint(e),
2615            Expression::XMLElement(e) => self.generate_xml_element(e),
2616            Expression::XMLGet(e) => self.generate_xml_get(e),
2617            Expression::XMLKeyValueOption(e) => self.generate_xml_key_value_option(e),
2618            Expression::XMLTable(e) => self.generate_xml_table(e),
2619            Expression::Xor(e) => self.generate_xor(e),
2620            Expression::Zipf(e) => self.generate_zipf(e),
2621            _ => {
2622                // Fallback for unimplemented expressions
2623                self.write(&format!("/* unimplemented: {:?} */", expr));
2624                Ok(())
2625            }
2626        }
2627    }
2628
2629    fn generate_select(&mut self, select: &Select) -> Result<()> {
2630        use crate::dialects::DialectType;
2631
2632        // Output leading comments before SELECT
2633        for comment in &select.leading_comments {
2634            self.write_formatted_comment(comment);
2635            self.write(" ");
2636        }
2637
2638        // WITH clause
2639        if let Some(with) = &select.with {
2640            self.generate_with(with)?;
2641            if self.config.pretty {
2642                self.write_newline();
2643                self.write_indent();
2644            } else {
2645                self.write_space();
2646            }
2647        }
2648
2649        // Output post-SELECT comments (comments that appeared after SELECT keyword)
2650        // These are output BEFORE SELECT, as Python SQLGlot normalizes them this way
2651        for comment in &select.post_select_comments {
2652            self.write_formatted_comment(comment);
2653            self.write(" ");
2654        }
2655
2656        self.write_keyword("SELECT");
2657
2658        // Generate query hint if present /*+ ... */
2659        if let Some(hint) = &select.hint {
2660            self.generate_hint(hint)?;
2661        }
2662
2663        // For SQL Server, convert LIMIT to TOP (structural transformation)
2664        // But only when there's no OFFSET (otherwise use OFFSET/FETCH syntax)
2665        // TOP clause (SQL Server style - before DISTINCT)
2666        let use_top_from_limit = matches!(self.config.dialect, Some(DialectType::TSQL))
2667            && select.top.is_none()
2668            && select.limit.is_some()
2669            && select.offset.is_none();  // Don't use TOP when there's OFFSET
2670
2671        // For TOP-supporting dialects: DISTINCT before TOP
2672        // For non-TOP dialects: TOP is converted to LIMIT later; DISTINCT goes here
2673        let is_top_dialect = matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Teradata) | Some(DialectType::Fabric));
2674
2675        if select.distinct && (is_top_dialect || select.top.is_some()) {
2676            self.write_space();
2677            self.write_keyword("DISTINCT");
2678        }
2679
2680        if is_top_dialect {
2681            if let Some(top) = &select.top {
2682                self.write_space();
2683                self.write_keyword("TOP");
2684                if top.parenthesized {
2685                    self.write(" (");
2686                    self.generate_expression(&top.this)?;
2687                    self.write(")");
2688                } else {
2689                    self.write_space();
2690                    self.generate_expression(&top.this)?;
2691                }
2692                if top.percent {
2693                    self.write_space();
2694                    self.write_keyword("PERCENT");
2695                }
2696                if top.with_ties {
2697                    self.write_space();
2698                    self.write_keyword("WITH TIES");
2699                }
2700            } else if use_top_from_limit {
2701                // Convert LIMIT to TOP for SQL Server (only when no OFFSET)
2702                if let Some(limit) = &select.limit {
2703                    self.write_space();
2704                    self.write_keyword("TOP");
2705                    // Use parentheses for complex expressions, but not for simple literals
2706                    let is_simple_literal = matches!(&limit.this, Expression::Literal(Literal::Number(_)));
2707                    if is_simple_literal {
2708                        self.write_space();
2709                        self.generate_expression(&limit.this)?;
2710                    } else {
2711                        self.write(" (");
2712                        self.generate_expression(&limit.this)?;
2713                        self.write(")");
2714                    }
2715                }
2716            }
2717        }
2718
2719        if select.distinct && !is_top_dialect && select.top.is_none() {
2720            self.write_space();
2721            self.write_keyword("DISTINCT");
2722        }
2723
2724        // DISTINCT ON clause (PostgreSQL)
2725        if let Some(distinct_on) = &select.distinct_on {
2726            self.write_space();
2727            self.write_keyword("ON");
2728            self.write(" (");
2729            for (i, expr) in distinct_on.iter().enumerate() {
2730                if i > 0 {
2731                    self.write(", ");
2732                }
2733                self.generate_expression(expr)?;
2734            }
2735            self.write(")");
2736        }
2737
2738        // MySQL operation modifiers (HIGH_PRIORITY, STRAIGHT_JOIN, SQL_CALC_FOUND_ROWS, etc.)
2739        for modifier in &select.operation_modifiers {
2740            self.write_space();
2741            self.write_keyword(modifier);
2742        }
2743
2744        // BigQuery SELECT AS STRUCT / SELECT AS VALUE
2745        if let Some(kind) = &select.kind {
2746            self.write_space();
2747            self.write_keyword("AS");
2748            self.write_space();
2749            self.write_keyword(kind);
2750        }
2751
2752        // Expressions (only if there are any)
2753        if !select.expressions.is_empty() {
2754            if self.config.pretty {
2755                self.write_newline();
2756                self.indent_level += 1;
2757            } else {
2758                self.write_space();
2759            }
2760        }
2761
2762        for (i, expr) in select.expressions.iter().enumerate() {
2763            if i > 0 {
2764                self.write(",");
2765                if self.config.pretty {
2766                    self.write_newline();
2767                } else {
2768                    self.write_space();
2769                }
2770            }
2771            if self.config.pretty {
2772                self.write_indent();
2773            }
2774            self.generate_expression(expr)?;
2775        }
2776
2777        if self.config.pretty && !select.expressions.is_empty() {
2778            self.indent_level -= 1;
2779        }
2780
2781        // INTO clause (SELECT ... INTO table_name)
2782        // Also handles Oracle PL/SQL: BULK COLLECT INTO v1, v2, ...
2783        if let Some(into) = &select.into {
2784            if self.config.pretty {
2785                self.write_newline();
2786                self.write_indent();
2787            } else {
2788                self.write_space();
2789            }
2790            if into.bulk_collect {
2791                self.write_keyword("BULK COLLECT INTO");
2792            } else {
2793                self.write_keyword("INTO");
2794            }
2795            if into.temporary {
2796                self.write_space();
2797                self.write_keyword("TEMPORARY");
2798            }
2799            if into.unlogged {
2800                self.write_space();
2801                self.write_keyword("UNLOGGED");
2802            }
2803            self.write_space();
2804            // If we have multiple expressions, output them comma-separated
2805            if !into.expressions.is_empty() {
2806                for (i, expr) in into.expressions.iter().enumerate() {
2807                    if i > 0 {
2808                        self.write(", ");
2809                    }
2810                    self.generate_expression(expr)?;
2811                }
2812            } else {
2813                self.generate_expression(&into.this)?;
2814            }
2815        }
2816
2817        // FROM clause
2818        if let Some(from) = &select.from {
2819            if self.config.pretty {
2820                self.write_newline();
2821                self.write_indent();
2822            } else {
2823                self.write_space();
2824            }
2825            self.write_keyword("FROM");
2826            self.write_space();
2827
2828            // BigQuery, Hive, Spark, Databricks, SQLite, and ClickHouse prefer explicit CROSS JOIN over comma syntax for multiple tables
2829            // But keep commas when TABLESAMPLE is present (Spark/Hive handle TABLESAMPLE differently with commas)
2830            let has_tablesample = from.expressions.iter().any(|e| matches!(e, Expression::TableSample(_)));
2831            let use_cross_join = !has_tablesample && matches!(
2832                self.config.dialect,
2833                Some(DialectType::BigQuery)
2834                    | Some(DialectType::Hive)
2835                    | Some(DialectType::Spark)
2836                    | Some(DialectType::Databricks)
2837                    | Some(DialectType::SQLite)
2838                    | Some(DialectType::ClickHouse)
2839            );
2840
2841            // Snowflake wraps standalone VALUES in FROM clause with parentheses
2842            let wrap_values_in_parens = matches!(
2843                self.config.dialect,
2844                Some(DialectType::Snowflake)
2845            );
2846
2847            for (i, expr) in from.expressions.iter().enumerate() {
2848                if i > 0 {
2849                    if use_cross_join {
2850                        self.write(" CROSS JOIN ");
2851                    } else {
2852                        self.write(", ");
2853                    }
2854                }
2855                if wrap_values_in_parens && matches!(expr, Expression::Values(_)) {
2856                    self.write("(");
2857                    self.generate_expression(expr)?;
2858                    self.write(")");
2859                } else {
2860                    self.generate_expression(expr)?;
2861                }
2862            }
2863        }
2864
2865        // JOINs
2866        for join in &select.joins {
2867            self.generate_join(join)?;
2868        }
2869
2870        // Output deferred ON/USING conditions (right-to-left, which is reverse order)
2871        // These are joins where the condition was specified after all JOINs
2872        for join in select.joins.iter().rev() {
2873            if join.deferred_condition {
2874                self.generate_join_condition(join)?;
2875            }
2876        }
2877
2878        // LATERAL VIEW clauses (Hive/Spark)
2879        for lateral_view in &select.lateral_views {
2880            self.generate_lateral_view(lateral_view)?;
2881        }
2882
2883        // PREWHERE (ClickHouse)
2884        if let Some(prewhere) = &select.prewhere {
2885            self.write_clause_condition("PREWHERE", prewhere)?;
2886        }
2887
2888        // WHERE
2889        if let Some(where_clause) = &select.where_clause {
2890            self.write_clause_condition("WHERE", &where_clause.this)?;
2891        }
2892
2893        // CONNECT BY (Oracle hierarchical queries)
2894        if let Some(connect) = &select.connect {
2895            self.generate_connect(connect)?;
2896        }
2897
2898        // GROUP BY
2899        if let Some(group_by) = &select.group_by {
2900            if self.config.pretty {
2901                self.write_newline();
2902                self.write_indent();
2903            } else {
2904                self.write_space();
2905            }
2906            self.write_keyword("GROUP BY");
2907            // Handle ALL/DISTINCT modifier: Some(true) = ALL, Some(false) = DISTINCT
2908            match group_by.all {
2909                Some(true) => {
2910                    self.write_space();
2911                    self.write_keyword("ALL");
2912                }
2913                Some(false) => {
2914                    self.write_space();
2915                    self.write_keyword("DISTINCT");
2916                }
2917                None => {}
2918            }
2919            if !group_by.expressions.is_empty() {
2920                // Check for trailing WITH CUBE or WITH ROLLUP (Hive/MySQL syntax)
2921                // These are represented as Cube/Rollup expressions with empty expressions at the end
2922                let mut trailing_cube = false;
2923                let mut trailing_rollup = false;
2924                let mut plain_expressions: Vec<&Expression> = Vec::new();
2925                let mut grouping_sets_expressions: Vec<&Expression> = Vec::new();
2926                let mut cube_expressions: Vec<&Expression> = Vec::new();
2927                let mut rollup_expressions: Vec<&Expression> = Vec::new();
2928
2929                for expr in &group_by.expressions {
2930                    match expr {
2931                        Expression::Cube(c) if c.expressions.is_empty() => {
2932                            trailing_cube = true;
2933                        }
2934                        Expression::Rollup(r) if r.expressions.is_empty() => {
2935                            trailing_rollup = true;
2936                        }
2937                        Expression::Function(f) if f.name == "CUBE" => {
2938                            cube_expressions.push(expr);
2939                        }
2940                        Expression::Function(f) if f.name == "ROLLUP" => {
2941                            rollup_expressions.push(expr);
2942                        }
2943                        Expression::Function(f) if f.name == "GROUPING SETS" => {
2944                            grouping_sets_expressions.push(expr);
2945                        }
2946                        _ => {
2947                            plain_expressions.push(expr);
2948                        }
2949                    }
2950                }
2951
2952                // Reorder: plain expressions first, then GROUPING SETS, CUBE, ROLLUP
2953                let mut regular_expressions: Vec<&Expression> = Vec::new();
2954                regular_expressions.extend(plain_expressions);
2955                regular_expressions.extend(grouping_sets_expressions);
2956                regular_expressions.extend(cube_expressions);
2957                regular_expressions.extend(rollup_expressions);
2958
2959                if self.config.pretty {
2960                    self.write_newline();
2961                    self.indent_level += 1;
2962                    self.write_indent();
2963                } else {
2964                    self.write_space();
2965                }
2966
2967                for (i, expr) in regular_expressions.iter().enumerate() {
2968                    if i > 0 {
2969                        self.write(", ");
2970                    }
2971                    self.generate_expression(expr)?;
2972                }
2973
2974                if self.config.pretty {
2975                    self.indent_level -= 1;
2976                }
2977
2978                // Output trailing WITH CUBE or WITH ROLLUP
2979                if trailing_cube {
2980                    self.write_space();
2981                    self.write_keyword("WITH CUBE");
2982                } else if trailing_rollup {
2983                    self.write_space();
2984                    self.write_keyword("WITH ROLLUP");
2985                }
2986            }
2987
2988            // ClickHouse: WITH TOTALS
2989            if group_by.totals {
2990                self.write_space();
2991                self.write_keyword("WITH TOTALS");
2992            }
2993        }
2994
2995        // HAVING
2996        if let Some(having) = &select.having {
2997            self.write_clause_condition("HAVING", &having.this)?;
2998        }
2999
3000        // QUALIFY and WINDOW clause ordering depends on input SQL
3001        if select.qualify_after_window {
3002            // WINDOW before QUALIFY (DuckDB style)
3003            if let Some(windows) = &select.windows {
3004                self.write_window_clause(windows)?;
3005            }
3006            if let Some(qualify) = &select.qualify {
3007                self.write_clause_condition("QUALIFY", &qualify.this)?;
3008            }
3009        } else {
3010            // QUALIFY before WINDOW (Snowflake/BigQuery default)
3011            if let Some(qualify) = &select.qualify {
3012                self.write_clause_condition("QUALIFY", &qualify.this)?;
3013            }
3014            if let Some(windows) = &select.windows {
3015                self.write_window_clause(windows)?;
3016            }
3017        }
3018
3019        // DISTRIBUTE BY (Hive/Spark)
3020        if let Some(distribute_by) = &select.distribute_by {
3021            self.write_clause_expressions("DISTRIBUTE BY", &distribute_by.expressions)?;
3022        }
3023
3024        // CLUSTER BY (Hive/Spark)
3025        if let Some(cluster_by) = &select.cluster_by {
3026            self.write_order_clause("CLUSTER BY", &cluster_by.expressions)?;
3027        }
3028
3029        // SORT BY (Hive/Spark - comes before ORDER BY)
3030        if let Some(sort_by) = &select.sort_by {
3031            self.write_order_clause("SORT BY", &sort_by.expressions)?;
3032        }
3033
3034        // ORDER BY (or ORDER SIBLINGS BY for Oracle hierarchical queries)
3035        if let Some(order_by) = &select.order_by {
3036            let keyword = if order_by.siblings { "ORDER SIBLINGS BY" } else { "ORDER BY" };
3037            self.write_order_clause(keyword, &order_by.expressions)?;
3038        }
3039
3040        // TSQL: FETCH requires ORDER BY. If there's a FETCH but no ORDER BY, add ORDER BY (SELECT NULL) OFFSET 0 ROWS
3041        if select.order_by.is_none() && select.fetch.is_some()
3042            && matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric))
3043        {
3044            if self.config.pretty {
3045                self.write_newline();
3046                self.write_indent();
3047            } else {
3048                self.write_space();
3049            }
3050            self.write_keyword("ORDER BY (SELECT NULL) OFFSET 0 ROWS");
3051        }
3052
3053        // LIMIT and OFFSET
3054        // PostgreSQL and others use: LIMIT count OFFSET offset
3055        // SQL Server uses: OFFSET ... FETCH (no LIMIT)
3056        // Presto/Trino uses: OFFSET n LIMIT m (offset before limit)
3057        let is_presto_like = matches!(
3058            self.config.dialect,
3059            Some(DialectType::Presto) | Some(DialectType::Trino)
3060        );
3061
3062        if is_presto_like && select.offset.is_some() {
3063            // Presto/Trino syntax: OFFSET n LIMIT m (offset comes first)
3064            if let Some(offset) = &select.offset {
3065                if self.config.pretty {
3066                    self.write_newline();
3067                    self.write_indent();
3068                } else {
3069                    self.write_space();
3070                }
3071                self.write_keyword("OFFSET");
3072                self.write_space();
3073                self.write_limit_expr(&offset.this)?;
3074                if offset.rows == Some(true) {
3075                    self.write_space();
3076                    self.write_keyword("ROWS");
3077                }
3078            }
3079            if let Some(limit) = &select.limit {
3080                if self.config.pretty {
3081                    self.write_newline();
3082                    self.write_indent();
3083                } else {
3084                    self.write_space();
3085                }
3086                self.write_keyword("LIMIT");
3087                self.write_space();
3088                self.write_limit_expr(&limit.this)?;
3089                if limit.percent {
3090                    self.write_space();
3091                    self.write_keyword("PERCENT");
3092                }
3093            }
3094        } else {
3095            // Check if FETCH will be converted to LIMIT (used for ordering)
3096            let fetch_as_limit = select.fetch.as_ref().map_or(false, |fetch| {
3097                !fetch.percent && !fetch.with_ties && fetch.count.is_some() && matches!(
3098                    self.config.dialect,
3099                    Some(DialectType::Spark) | Some(DialectType::Hive)
3100                    | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
3101                    | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
3102                    | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
3103                    | Some(DialectType::Redshift)
3104                )
3105            });
3106
3107            // Standard LIMIT clause (skip for SQL Server - we use TOP or OFFSET/FETCH instead)
3108            if let Some(limit) = &select.limit {
3109                // SQL Server uses TOP (no OFFSET) or OFFSET/FETCH (with OFFSET) instead of LIMIT
3110                if !matches!(self.config.dialect, Some(DialectType::TSQL)) {
3111                    if self.config.pretty {
3112                        self.write_newline();
3113                        self.write_indent();
3114                    } else {
3115                        self.write_space();
3116                    }
3117                    self.write_keyword("LIMIT");
3118                    self.write_space();
3119                    self.write_limit_expr(&limit.this)?;
3120                    if limit.percent {
3121                        self.write_space();
3122                        self.write_keyword("PERCENT");
3123                    }
3124                }
3125            }
3126
3127            // Convert TOP to LIMIT for non-TOP dialects
3128            if select.top.is_some() && !is_top_dialect && select.limit.is_none() {
3129                if let Some(top) = &select.top {
3130                    if !top.percent && !top.with_ties {
3131                        if self.config.pretty {
3132                            self.write_newline();
3133                            self.write_indent();
3134                        } else {
3135                            self.write_space();
3136                        }
3137                        self.write_keyword("LIMIT");
3138                        self.write_space();
3139                        self.generate_expression(&top.this)?;
3140                    }
3141                }
3142            }
3143
3144            // If FETCH will be converted to LIMIT and there's also OFFSET,
3145            // emit LIMIT from FETCH BEFORE the OFFSET
3146            if fetch_as_limit && select.offset.is_some() {
3147                if let Some(fetch) = &select.fetch {
3148                    if self.config.pretty {
3149                        self.write_newline();
3150                        self.write_indent();
3151                    } else {
3152                        self.write_space();
3153                    }
3154                    self.write_keyword("LIMIT");
3155                    self.write_space();
3156                    self.generate_expression(fetch.count.as_ref().unwrap())?;
3157                }
3158            }
3159
3160            // OFFSET
3161            // In SQL Server, OFFSET requires ORDER BY and uses different syntax
3162            // OFFSET x ROWS FETCH NEXT y ROWS ONLY
3163            if let Some(offset) = &select.offset {
3164                if self.config.pretty {
3165                    self.write_newline();
3166                    self.write_indent();
3167                } else {
3168                    self.write_space();
3169                }
3170                if matches!(self.config.dialect, Some(DialectType::TSQL)) {
3171                    // SQL Server 2012+ OFFSET ... FETCH syntax
3172                    self.write_keyword("OFFSET");
3173                    self.write_space();
3174                    self.write_limit_expr(&offset.this)?;
3175                    self.write_space();
3176                    self.write_keyword("ROWS");
3177                    // If there was a LIMIT, use FETCH NEXT ... ROWS ONLY
3178                    if let Some(limit) = &select.limit {
3179                        self.write_space();
3180                        self.write_keyword("FETCH NEXT");
3181                        self.write_space();
3182                        self.write_limit_expr(&limit.this)?;
3183                        self.write_space();
3184                        self.write_keyword("ROWS ONLY");
3185                    }
3186                } else {
3187                    self.write_keyword("OFFSET");
3188                    self.write_space();
3189                    self.write_limit_expr(&offset.this)?;
3190                    // Output ROWS keyword if it was in the original SQL
3191                    if offset.rows == Some(true) {
3192                        self.write_space();
3193                        self.write_keyword("ROWS");
3194                    }
3195                }
3196            }
3197        }
3198
3199        // ClickHouse LIMIT BY clause (after LIMIT/OFFSET)
3200        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
3201            if let Some(limit_by) = &select.limit_by {
3202                if !limit_by.is_empty() {
3203                    self.write_space();
3204                    self.write_keyword("BY");
3205                    self.write_space();
3206                    for (i, expr) in limit_by.iter().enumerate() {
3207                        if i > 0 {
3208                            self.write(", ");
3209                        }
3210                        self.generate_expression(expr)?;
3211                    }
3212                }
3213            }
3214        }
3215
3216        // ClickHouse SETTINGS and FORMAT modifiers (after LIMIT/OFFSET)
3217        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
3218            if let Some(settings) = &select.settings {
3219                if self.config.pretty {
3220                    self.write_newline();
3221                    self.write_indent();
3222                } else {
3223                    self.write_space();
3224                }
3225                self.write_keyword("SETTINGS");
3226                self.write_space();
3227                for (i, expr) in settings.iter().enumerate() {
3228                    if i > 0 {
3229                        self.write(", ");
3230                    }
3231                    self.generate_expression(expr)?;
3232                }
3233            }
3234
3235            if let Some(format_expr) = &select.format {
3236                if self.config.pretty {
3237                    self.write_newline();
3238                    self.write_indent();
3239                } else {
3240                    self.write_space();
3241                }
3242                self.write_keyword("FORMAT");
3243                self.write_space();
3244                self.generate_expression(format_expr)?;
3245            }
3246        }
3247
3248        // FETCH FIRST/NEXT
3249        if let Some(fetch) = &select.fetch {
3250            // Check if we already emitted LIMIT from FETCH before OFFSET
3251            let fetch_already_as_limit = select.offset.is_some() && !fetch.percent && !fetch.with_ties && fetch.count.is_some() && matches!(
3252                self.config.dialect,
3253                Some(DialectType::Spark) | Some(DialectType::Hive)
3254                | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
3255                | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
3256                | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
3257                | Some(DialectType::Redshift)
3258            );
3259
3260            if fetch_already_as_limit {
3261                // Already emitted as LIMIT before OFFSET, skip
3262            } else {
3263            if self.config.pretty {
3264                self.write_newline();
3265                self.write_indent();
3266            } else {
3267                self.write_space();
3268            }
3269
3270            // Convert FETCH to LIMIT for dialects that prefer LIMIT syntax
3271            let use_limit = !fetch.percent && !fetch.with_ties && fetch.count.is_some() && matches!(
3272                self.config.dialect,
3273                Some(DialectType::Spark) | Some(DialectType::Hive)
3274                | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
3275                | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
3276                | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
3277                | Some(DialectType::Redshift)
3278            );
3279
3280            if use_limit {
3281                self.write_keyword("LIMIT");
3282                self.write_space();
3283                self.generate_expression(fetch.count.as_ref().unwrap())?;
3284            } else {
3285                self.write_keyword("FETCH");
3286                self.write_space();
3287                self.write_keyword(&fetch.direction);
3288                if let Some(ref count) = fetch.count {
3289                    self.write_space();
3290                    self.generate_expression(count)?;
3291                }
3292                if fetch.percent {
3293                    self.write_space();
3294                    self.write_keyword("PERCENT");
3295                }
3296                if fetch.rows {
3297                    self.write_space();
3298                    self.write_keyword("ROWS");
3299                }
3300                if fetch.with_ties {
3301                    self.write_space();
3302                    self.write_keyword("WITH TIES");
3303                } else {
3304                    self.write_space();
3305                    self.write_keyword("ONLY");
3306                }
3307            }
3308            } // close fetch_already_as_limit else
3309        }
3310
3311        // SAMPLE / TABLESAMPLE
3312        if let Some(sample) = &select.sample {
3313            use crate::dialects::DialectType;
3314            if self.config.pretty {
3315                self.write_newline();
3316            } else {
3317                self.write_space();
3318            }
3319
3320            if sample.is_using_sample {
3321                // DuckDB USING SAMPLE: METHOD (size UNIT) [REPEATABLE (seed)]
3322                self.write_keyword("USING SAMPLE");
3323                self.generate_sample_body(sample)?;
3324            } else {
3325                self.write_keyword("TABLESAMPLE");
3326
3327                // Snowflake defaults to BERNOULLI when no explicit method is given
3328                let snowflake_bernoulli = matches!(self.config.dialect, Some(DialectType::Snowflake)) && !sample.explicit_method;
3329                if snowflake_bernoulli {
3330                    self.write_space();
3331                    self.write_keyword("BERNOULLI");
3332                }
3333
3334                // Handle BUCKET sampling: TABLESAMPLE (BUCKET 1 OUT OF 5 ON x)
3335                if matches!(sample.method, SampleMethod::Bucket) {
3336                    self.write_space();
3337                    self.write("(");
3338                    self.write_keyword("BUCKET");
3339                    self.write_space();
3340                    if let Some(ref num) = sample.bucket_numerator {
3341                        self.generate_expression(num)?;
3342                    }
3343                    self.write_space();
3344                    self.write_keyword("OUT OF");
3345                    self.write_space();
3346                    if let Some(ref denom) = sample.bucket_denominator {
3347                        self.generate_expression(denom)?;
3348                    }
3349                    if let Some(ref field) = sample.bucket_field {
3350                        self.write_space();
3351                        self.write_keyword("ON");
3352                        self.write_space();
3353                        self.generate_expression(field)?;
3354                    }
3355                    self.write(")");
3356                } else if sample.unit_after_size {
3357                    // Syntax: TABLESAMPLE [METHOD] (size ROWS) or TABLESAMPLE [METHOD] (size PERCENT)
3358                    if sample.explicit_method && sample.method_before_size {
3359                        self.write_space();
3360                        match sample.method {
3361                            SampleMethod::Bernoulli => self.write_keyword("BERNOULLI"),
3362                            SampleMethod::System => self.write_keyword("SYSTEM"),
3363                            SampleMethod::Block => self.write_keyword("BLOCK"),
3364                            SampleMethod::Row => self.write_keyword("ROW"),
3365                            SampleMethod::Reservoir => self.write_keyword("RESERVOIR"),
3366                            _ => {}
3367                        }
3368                    }
3369                    self.write(" (");
3370                    self.generate_expression(&sample.size)?;
3371                    self.write_space();
3372                    match sample.method {
3373                        SampleMethod::Percent => self.write_keyword("PERCENT"),
3374                        SampleMethod::Row => self.write_keyword("ROWS"),
3375                        SampleMethod::Reservoir => self.write_keyword("ROWS"),
3376                        _ => {
3377                            self.write_keyword("PERCENT");
3378                        }
3379                    }
3380                    self.write(")");
3381                } else {
3382                    // Syntax: TABLESAMPLE METHOD (size)
3383                    self.write_space();
3384                    match sample.method {
3385                        SampleMethod::Bernoulli => self.write_keyword("BERNOULLI"),
3386                        SampleMethod::System => self.write_keyword("SYSTEM"),
3387                        SampleMethod::Block => self.write_keyword("BLOCK"),
3388                        SampleMethod::Row => self.write_keyword("ROW"),
3389                        SampleMethod::Percent => self.write_keyword("BERNOULLI"),
3390                        SampleMethod::Bucket => {}
3391                        SampleMethod::Reservoir => self.write_keyword("RESERVOIR"),
3392                    }
3393                    self.write(" (");
3394                    self.generate_expression(&sample.size)?;
3395                    if matches!(sample.method, SampleMethod::Percent) {
3396                        self.write_space();
3397                        self.write_keyword("PERCENT");
3398                    }
3399                    self.write(")");
3400                }
3401            }
3402
3403            if let Some(seed) = &sample.seed {
3404                self.write_space();
3405                // Databricks/Spark use REPEATABLE, not SEED
3406                let use_seed = sample.use_seed_keyword
3407                    && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
3408                if use_seed {
3409                    self.write_keyword("SEED");
3410                } else {
3411                    self.write_keyword("REPEATABLE");
3412                }
3413                self.write(" (");
3414                self.generate_expression(seed)?;
3415                self.write(")");
3416            }
3417        }
3418
3419        // FOR UPDATE/SHARE locks
3420        // Skip locking clauses for dialects that don't support them
3421        if self.config.locking_reads_supported {
3422            for lock in &select.locks {
3423                if self.config.pretty {
3424                    self.write_newline();
3425                    self.write_indent();
3426                } else {
3427                    self.write_space();
3428                }
3429                self.generate_lock(lock)?;
3430            }
3431        }
3432
3433        // FOR XML clause (T-SQL)
3434        if !select.for_xml.is_empty() {
3435            if self.config.pretty {
3436                self.write_newline();
3437                self.write_indent();
3438            } else {
3439                self.write_space();
3440            }
3441            self.write_keyword("FOR XML");
3442            for (i, opt) in select.for_xml.iter().enumerate() {
3443                if self.config.pretty {
3444                    if i > 0 {
3445                        self.write(",");
3446                    }
3447                    self.write_newline();
3448                    self.write_indent();
3449                    self.write("  "); // extra indent for options
3450                } else {
3451                    if i > 0 {
3452                        self.write(",");
3453                    }
3454                    self.write_space();
3455                }
3456                self.generate_for_xml_option(opt)?;
3457            }
3458        }
3459
3460        // TSQL: OPTION clause
3461        if let Some(ref option) = select.option {
3462            if matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)) {
3463                self.write_space();
3464                self.write(option);
3465            }
3466        }
3467
3468        Ok(())
3469    }
3470
3471    /// Generate a single FOR XML option
3472    fn generate_for_xml_option(&mut self, opt: &Expression) -> Result<()> {
3473        match opt {
3474            Expression::QueryOption(qo) => {
3475                // Extract the option name from Var
3476                if let Expression::Var(var) = &*qo.this {
3477                    self.write(&var.this);
3478                } else {
3479                    self.generate_expression(&qo.this)?;
3480                }
3481                // If there's an expression (like PATH('element')), output it in parens
3482                if let Some(expr) = &qo.expression {
3483                    self.write("(");
3484                    self.generate_expression(expr)?;
3485                    self.write(")");
3486                }
3487            }
3488            _ => {
3489                self.generate_expression(opt)?;
3490            }
3491        }
3492        Ok(())
3493    }
3494
3495    fn generate_with(&mut self, with: &With) -> Result<()> {
3496        use crate::dialects::DialectType;
3497
3498        // Output leading comments before WITH
3499        for comment in &with.leading_comments {
3500            self.write(comment);
3501            self.write(" ");
3502        }
3503        self.write_keyword("WITH");
3504        if with.recursive {
3505            self.write_space();
3506            self.write_keyword("RECURSIVE");
3507        }
3508        self.write_space();
3509
3510        // BigQuery doesn't support column aliases in CTE definitions
3511        let skip_cte_columns = matches!(self.config.dialect, Some(DialectType::BigQuery));
3512
3513        for (i, cte) in with.ctes.iter().enumerate() {
3514            if i > 0 {
3515                self.write(",");
3516                if self.config.pretty {
3517                    self.write_space();
3518                } else {
3519                    self.write(" ");
3520                }
3521            }
3522            if matches!(self.config.dialect, Some(DialectType::ClickHouse)) && !cte.alias_first {
3523                self.generate_expression(&cte.this)?;
3524                self.write_space();
3525                self.write_keyword("AS");
3526                self.write_space();
3527                self.generate_identifier(&cte.alias)?;
3528                continue;
3529            }
3530            self.generate_identifier(&cte.alias)?;
3531            if !cte.columns.is_empty() && !skip_cte_columns {
3532                self.write("(");
3533                for (j, col) in cte.columns.iter().enumerate() {
3534                    if j > 0 {
3535                        self.write(", ");
3536                    }
3537                    self.generate_identifier(col)?;
3538                }
3539                self.write(")");
3540            }
3541            // USING KEY (columns) for DuckDB recursive CTEs
3542            if !cte.key_expressions.is_empty() {
3543                self.write_space();
3544                self.write_keyword("USING KEY");
3545                self.write(" (");
3546                for (i, key) in cte.key_expressions.iter().enumerate() {
3547                    if i > 0 {
3548                        self.write(", ");
3549                    }
3550                    self.generate_identifier(key)?;
3551                }
3552                self.write(")");
3553            }
3554            self.write_space();
3555            self.write_keyword("AS");
3556            // MATERIALIZED / NOT MATERIALIZED
3557            if let Some(materialized) = cte.materialized {
3558                self.write_space();
3559                if materialized {
3560                    self.write_keyword("MATERIALIZED");
3561                } else {
3562                    self.write_keyword("NOT MATERIALIZED");
3563                }
3564            }
3565            self.write(" (");
3566            if self.config.pretty {
3567                self.write_newline();
3568                self.indent_level += 1;
3569                self.write_indent();
3570            }
3571            // For Spark/Databricks, VALUES in a CTE must be wrapped with SELECT * FROM
3572            // e.g., WITH t AS (VALUES ('foo_val') AS t(foo1)) -> WITH t AS (SELECT * FROM VALUES ('foo_val') AS t(foo1))
3573            let wrap_values_in_select = matches!(
3574                self.config.dialect,
3575                Some(DialectType::Spark) | Some(DialectType::Databricks)
3576            ) && matches!(&cte.this, Expression::Values(_));
3577
3578            if wrap_values_in_select {
3579                self.write_keyword("SELECT");
3580                self.write(" * ");
3581                self.write_keyword("FROM");
3582                self.write_space();
3583            }
3584            self.generate_expression(&cte.this)?;
3585            if self.config.pretty {
3586                self.write_newline();
3587                self.indent_level -= 1;
3588                self.write_indent();
3589            }
3590            self.write(")");
3591        }
3592
3593        // Generate SEARCH/CYCLE clause if present
3594        if let Some(search) = &with.search {
3595            self.write_space();
3596            self.generate_expression(search)?;
3597        }
3598
3599        Ok(())
3600    }
3601
3602    fn generate_join(&mut self, join: &Join) -> Result<()> {
3603        // Implicit (comma) joins: output as ", table" instead of "CROSS JOIN table"
3604        if join.kind == JoinKind::Implicit {
3605            self.write(",");
3606            if self.config.pretty {
3607                self.write_newline();
3608                self.write_indent();
3609            } else {
3610                self.write_space();
3611            }
3612            self.generate_expression(&join.this)?;
3613            return Ok(());
3614        }
3615
3616        if self.config.pretty {
3617            self.write_newline();
3618            self.write_indent();
3619        } else {
3620            self.write_space();
3621        }
3622
3623        // Helper: format hint suffix (e.g., " LOOP" or "")
3624        // Only include join hints for dialects that support them
3625        let hint_str = if self.config.join_hints {
3626            join.join_hint.as_ref().map(|h| format!(" {}", h)).unwrap_or_default()
3627        } else {
3628            String::new()
3629        };
3630
3631        let clickhouse_join_keyword = if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
3632            if let Some(hint) = &join.join_hint {
3633                let mut global = false;
3634                let mut strictness: Option<&'static str> = None;
3635                for part in hint.split_whitespace() {
3636                    match part.to_uppercase().as_str() {
3637                        "GLOBAL" => global = true,
3638                        "ANY" => strictness = Some("ANY"),
3639                        "ASOF" => strictness = Some("ASOF"),
3640                        "SEMI" => strictness = Some("SEMI"),
3641                        "ANTI" => strictness = Some("ANTI"),
3642                        _ => {}
3643                    }
3644                }
3645
3646                if global || strictness.is_some() {
3647                    let join_type = match join.kind {
3648                        JoinKind::Left => {
3649                            if join.use_outer_keyword {
3650                                "LEFT OUTER"
3651                            } else if join.use_inner_keyword {
3652                                "LEFT INNER"
3653                            } else {
3654                                "LEFT"
3655                            }
3656                        }
3657                        JoinKind::Right => {
3658                            if join.use_outer_keyword {
3659                                "RIGHT OUTER"
3660                            } else if join.use_inner_keyword {
3661                                "RIGHT INNER"
3662                            } else {
3663                                "RIGHT"
3664                            }
3665                        }
3666                        JoinKind::Full => {
3667                            if join.use_outer_keyword {
3668                                "FULL OUTER"
3669                            } else {
3670                                "FULL"
3671                            }
3672                        }
3673                        JoinKind::Inner => {
3674                            if join.use_inner_keyword {
3675                                "INNER"
3676                            } else {
3677                                ""
3678                            }
3679                        }
3680                        _ => "",
3681                    };
3682
3683                    let mut parts = Vec::new();
3684                    if global {
3685                        parts.push("GLOBAL");
3686                    }
3687                    if !join_type.is_empty() {
3688                        parts.push(join_type);
3689                    }
3690                    if let Some(strict) = strictness {
3691                        parts.push(strict);
3692                    }
3693                    parts.push("JOIN");
3694                    Some(parts.join(" "))
3695                } else {
3696                    None
3697                }
3698            } else {
3699                None
3700            }
3701        } else {
3702            None
3703        };
3704
3705        if let Some(keyword) = clickhouse_join_keyword {
3706            self.write_keyword(&keyword);
3707        } else {
3708            match join.kind {
3709            JoinKind::Inner => {
3710                if join.use_inner_keyword {
3711                    self.write_keyword(&format!("INNER{} JOIN", hint_str));
3712                } else {
3713                    self.write_keyword(&format!("{}JOIN", if hint_str.is_empty() { String::new() } else { format!("{} ", hint_str.trim()) }));
3714                }
3715            }
3716            JoinKind::Left => {
3717                if join.use_outer_keyword {
3718                    self.write_keyword(&format!("LEFT OUTER{} JOIN", hint_str));
3719                } else if join.use_inner_keyword {
3720                    self.write_keyword(&format!("LEFT INNER{} JOIN", hint_str));
3721                } else {
3722                    self.write_keyword(&format!("LEFT{} JOIN", hint_str));
3723                }
3724            }
3725            JoinKind::Right => {
3726                if join.use_outer_keyword {
3727                    self.write_keyword(&format!("RIGHT OUTER{} JOIN", hint_str));
3728                } else if join.use_inner_keyword {
3729                    self.write_keyword(&format!("RIGHT INNER{} JOIN", hint_str));
3730                } else {
3731                    self.write_keyword(&format!("RIGHT{} JOIN", hint_str));
3732                }
3733            }
3734            JoinKind::Full => {
3735                if join.use_outer_keyword {
3736                    self.write_keyword(&format!("FULL OUTER{} JOIN", hint_str));
3737                } else {
3738                    self.write_keyword(&format!("FULL{} JOIN", hint_str));
3739                }
3740            }
3741            JoinKind::Outer => self.write_keyword("OUTER JOIN"),
3742            JoinKind::Cross => self.write_keyword("CROSS JOIN"),
3743            JoinKind::Natural => self.write_keyword("NATURAL JOIN"),
3744            JoinKind::NaturalLeft => {
3745                if join.use_outer_keyword {
3746                    self.write_keyword("NATURAL LEFT OUTER JOIN");
3747                } else {
3748                    self.write_keyword("NATURAL LEFT JOIN");
3749                }
3750            }
3751            JoinKind::NaturalRight => {
3752                if join.use_outer_keyword {
3753                    self.write_keyword("NATURAL RIGHT OUTER JOIN");
3754                } else {
3755                    self.write_keyword("NATURAL RIGHT JOIN");
3756                }
3757            }
3758            JoinKind::NaturalFull => {
3759                if join.use_outer_keyword {
3760                    self.write_keyword("NATURAL FULL OUTER JOIN");
3761                } else {
3762                    self.write_keyword("NATURAL FULL JOIN");
3763                }
3764            }
3765            JoinKind::Semi => self.write_keyword("SEMI JOIN"),
3766            JoinKind::Anti => self.write_keyword("ANTI JOIN"),
3767            JoinKind::LeftSemi => self.write_keyword("LEFT SEMI JOIN"),
3768            JoinKind::LeftAnti => self.write_keyword("LEFT ANTI JOIN"),
3769            JoinKind::RightSemi => self.write_keyword("RIGHT SEMI JOIN"),
3770            JoinKind::RightAnti => self.write_keyword("RIGHT ANTI JOIN"),
3771            JoinKind::CrossApply => {
3772                // CROSS APPLY -> INNER JOIN LATERAL for non-TSQL dialects
3773                if matches!(self.config.dialect, Some(DialectType::TSQL) | None) {
3774                    self.write_keyword("CROSS APPLY");
3775                } else {
3776                    self.write_keyword("INNER JOIN LATERAL");
3777                }
3778            }
3779            JoinKind::OuterApply => {
3780                // OUTER APPLY -> LEFT JOIN LATERAL for non-TSQL dialects
3781                if matches!(self.config.dialect, Some(DialectType::TSQL) | None) {
3782                    self.write_keyword("OUTER APPLY");
3783                } else {
3784                    self.write_keyword("LEFT JOIN LATERAL");
3785                }
3786            }
3787            JoinKind::AsOf => self.write_keyword("ASOF JOIN"),
3788            JoinKind::AsOfLeft => {
3789                if join.use_outer_keyword {
3790                    self.write_keyword("ASOF LEFT OUTER JOIN");
3791                } else {
3792                    self.write_keyword("ASOF LEFT JOIN");
3793                }
3794            }
3795            JoinKind::AsOfRight => {
3796                if join.use_outer_keyword {
3797                    self.write_keyword("ASOF RIGHT OUTER JOIN");
3798                } else {
3799                    self.write_keyword("ASOF RIGHT JOIN");
3800                }
3801            }
3802            JoinKind::Lateral => self.write_keyword("LATERAL JOIN"),
3803            JoinKind::LeftLateral => {
3804                if join.use_outer_keyword {
3805                    self.write_keyword("LEFT OUTER LATERAL JOIN");
3806                } else {
3807                    self.write_keyword("LEFT LATERAL JOIN");
3808                }
3809            }
3810            JoinKind::Straight => self.write_keyword("STRAIGHT_JOIN"),
3811            JoinKind::Implicit => {
3812                // BigQuery, Hive, Spark, and Databricks prefer explicit CROSS JOIN over comma syntax
3813                use crate::dialects::DialectType;
3814                if matches!(self.config.dialect, Some(DialectType::BigQuery) | Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)) {
3815                    self.write_keyword("CROSS JOIN");
3816                } else {
3817                    // Implicit join uses comma: FROM a, b
3818                    // We already wrote a space before the match, so replace with comma
3819                    // by removing trailing space and writing ", "
3820                    self.output.truncate(self.output.trim_end().len());
3821                    self.write(",");
3822                }
3823            }
3824            JoinKind::Array => self.write_keyword("ARRAY JOIN"),
3825            JoinKind::LeftArray => self.write_keyword("LEFT ARRAY JOIN"),
3826        }
3827        }
3828
3829        // ARRAY JOIN items need comma-separated output (Tuple holds multiple items)
3830        if matches!(join.kind, JoinKind::Array | JoinKind::LeftArray) {
3831            self.write_space();
3832            match &join.this {
3833                Expression::Tuple(t) => {
3834                    for (i, item) in t.expressions.iter().enumerate() {
3835                        if i > 0 {
3836                            self.write(", ");
3837                        }
3838                        self.generate_expression(item)?;
3839                    }
3840                }
3841                other => {
3842                    self.generate_expression(other)?;
3843                }
3844            }
3845        } else {
3846        self.write_space();
3847        self.generate_expression(&join.this)?;
3848        }
3849
3850        // Only output MATCH_CONDITION/ON/USING inline if the condition wasn't deferred
3851        if !join.deferred_condition {
3852            // Output MATCH_CONDITION first (Snowflake ASOF JOIN)
3853            if let Some(match_cond) = &join.match_condition {
3854                self.write_space();
3855                self.write_keyword("MATCH_CONDITION");
3856                self.write(" (");
3857                self.generate_expression(match_cond)?;
3858                self.write(")");
3859            }
3860
3861            if let Some(on) = &join.on {
3862                if self.config.pretty {
3863                    self.write_newline();
3864                    self.indent_level += 1;
3865                    self.write_indent();
3866                    self.write_keyword("ON");
3867                    self.write_space();
3868                    self.generate_join_on_condition(on)?;
3869                    self.indent_level -= 1;
3870                } else {
3871                    self.write_space();
3872                    self.write_keyword("ON");
3873                    self.write_space();
3874                    self.generate_expression(on)?;
3875                }
3876            }
3877
3878            if !join.using.is_empty() {
3879                if self.config.pretty {
3880                    self.write_newline();
3881                    self.indent_level += 1;
3882                    self.write_indent();
3883                    self.write_keyword("USING");
3884                    self.write(" (");
3885                    for (i, col) in join.using.iter().enumerate() {
3886                        if i > 0 {
3887                            self.write(", ");
3888                        }
3889                        self.generate_identifier(col)?;
3890                    }
3891                    self.write(")");
3892                    self.indent_level -= 1;
3893                } else {
3894                    self.write_space();
3895                    self.write_keyword("USING");
3896                    self.write(" (");
3897                    for (i, col) in join.using.iter().enumerate() {
3898                        if i > 0 {
3899                            self.write(", ");
3900                        }
3901                        self.generate_identifier(col)?;
3902                    }
3903                    self.write(")");
3904                }
3905            }
3906        }
3907
3908        // Generate PIVOT/UNPIVOT expressions that follow this join
3909        for pivot in &join.pivots {
3910            self.write_space();
3911            self.generate_expression(pivot)?;
3912        }
3913
3914        Ok(())
3915    }
3916
3917    /// Generate just the ON/USING/MATCH_CONDITION for a join (used for deferred conditions)
3918    fn generate_join_condition(&mut self, join: &Join) -> Result<()> {
3919        // Generate MATCH_CONDITION first (Snowflake ASOF JOIN)
3920        if let Some(match_cond) = &join.match_condition {
3921            self.write_space();
3922            self.write_keyword("MATCH_CONDITION");
3923            self.write(" (");
3924            self.generate_expression(match_cond)?;
3925            self.write(")");
3926        }
3927
3928        if let Some(on) = &join.on {
3929            if self.config.pretty {
3930                self.write_newline();
3931                self.indent_level += 1;
3932                self.write_indent();
3933                self.write_keyword("ON");
3934                self.write_space();
3935                // In pretty mode, split AND conditions onto separate lines
3936                self.generate_join_on_condition(on)?;
3937                self.indent_level -= 1;
3938            } else {
3939                self.write_space();
3940                self.write_keyword("ON");
3941                self.write_space();
3942                self.generate_expression(on)?;
3943            }
3944        }
3945
3946        if !join.using.is_empty() {
3947            if self.config.pretty {
3948                self.write_newline();
3949                self.indent_level += 1;
3950                self.write_indent();
3951                self.write_keyword("USING");
3952                self.write(" (");
3953                for (i, col) in join.using.iter().enumerate() {
3954                    if i > 0 {
3955                        self.write(", ");
3956                    }
3957                    self.generate_identifier(col)?;
3958                }
3959                self.write(")");
3960                self.indent_level -= 1;
3961            } else {
3962                self.write_space();
3963                self.write_keyword("USING");
3964                self.write(" (");
3965                for (i, col) in join.using.iter().enumerate() {
3966                    if i > 0 {
3967                        self.write(", ");
3968                    }
3969                    self.generate_identifier(col)?;
3970                }
3971                self.write(")");
3972            }
3973        }
3974
3975        // Generate PIVOT/UNPIVOT expressions that follow this join (for deferred conditions)
3976        for pivot in &join.pivots {
3977            self.write_space();
3978            self.generate_expression(pivot)?;
3979        }
3980
3981        Ok(())
3982    }
3983
3984    /// Generate JOIN ON condition with AND clauses on separate lines in pretty mode
3985    fn generate_join_on_condition(&mut self, expr: &Expression) -> Result<()> {
3986        // If it's an AND expression, split each condition onto a new line
3987        if let Expression::And(and_op) = expr {
3988            // Generate left side (might be another AND)
3989            self.generate_join_on_condition(&and_op.left)?;
3990            // AND on new line
3991            self.write_newline();
3992            self.write_indent();
3993            self.write_keyword("AND");
3994            self.write_space();
3995            // Generate right side (should be a single condition)
3996            self.generate_expression(&and_op.right)?;
3997        } else {
3998            // Base case: single condition
3999            self.generate_expression(expr)?;
4000        }
4001        Ok(())
4002    }
4003
4004    fn generate_joined_table(&mut self, jt: &JoinedTable) -> Result<()> {
4005        // Parenthesized join: (tbl1 CROSS JOIN tbl2)
4006        self.write("(");
4007        self.generate_expression(&jt.left)?;
4008
4009        // Generate all joins
4010        for join in &jt.joins {
4011            self.generate_join(join)?;
4012        }
4013
4014        // Generate LATERAL VIEW clauses (Hive/Spark)
4015        for lv in &jt.lateral_views {
4016            self.generate_lateral_view(lv)?;
4017        }
4018
4019        self.write(")");
4020
4021        // Alias
4022        if let Some(alias) = &jt.alias {
4023            self.write_space();
4024            self.write_keyword("AS");
4025            self.write_space();
4026            self.generate_identifier(alias)?;
4027        }
4028
4029        Ok(())
4030    }
4031
4032    fn generate_lateral_view(&mut self, lv: &LateralView) -> Result<()> {
4033        use crate::dialects::DialectType;
4034
4035        if self.config.pretty {
4036            self.write_newline();
4037            self.write_indent();
4038        } else {
4039            self.write_space();
4040        }
4041
4042        // For Hive/Spark/Databricks (or no dialect specified), output native LATERAL VIEW syntax
4043        // For PostgreSQL and other specific dialects, convert to CROSS JOIN (LATERAL or UNNEST)
4044        let use_lateral_join = matches!(
4045            self.config.dialect,
4046            Some(DialectType::PostgreSQL) | Some(DialectType::DuckDB) |
4047            Some(DialectType::Snowflake) | Some(DialectType::TSQL) |
4048            Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena)
4049        );
4050
4051        // Check if target dialect should use UNNEST instead of EXPLODE
4052        let use_unnest = matches!(
4053            self.config.dialect,
4054            Some(DialectType::DuckDB) | Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena)
4055        );
4056
4057        // Check if we need POSEXPLODE -> UNNEST WITH ORDINALITY
4058        let (is_posexplode, func_args) = match &lv.this {
4059            Expression::Explode(uf) => {
4060                // Expression::Explode is the dedicated EXPLODE expression type
4061                (false, vec![uf.this.clone()])
4062            }
4063            Expression::Function(func) => {
4064                let name = func.name.to_uppercase();
4065                if name == "POSEXPLODE" || name == "POSEXPLODE_OUTER" {
4066                    (true, func.args.clone())
4067                } else if name == "EXPLODE" || name == "EXPLODE_OUTER" || name == "INLINE" {
4068                    (false, func.args.clone())
4069                } else {
4070                    (false, vec![])
4071                }
4072            }
4073            _ => (false, vec![]),
4074        };
4075
4076        if use_lateral_join {
4077            // Convert to CROSS JOIN for PostgreSQL-like dialects
4078            if lv.outer {
4079                self.write_keyword("LEFT JOIN LATERAL");
4080            } else {
4081                self.write_keyword("CROSS JOIN");
4082            }
4083            self.write_space();
4084
4085            if use_unnest && !func_args.is_empty() {
4086                // Convert EXPLODE(y) -> UNNEST(y), POSEXPLODE(y) -> UNNEST(y)
4087                // For DuckDB, also convert ARRAY(y) -> [y]
4088                let unnest_args = if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
4089                    // DuckDB: ARRAY(y) -> [y]
4090                    func_args.iter().map(|a| {
4091                        if let Expression::Function(ref f) = a {
4092                            if f.name.to_uppercase() == "ARRAY" && f.args.len() == 1 {
4093                                return Expression::ArrayFunc(Box::new(crate::expressions::ArrayConstructor {
4094                                    expressions: f.args.clone(),
4095                                    bracket_notation: true,
4096                                    use_list_keyword: false,
4097                                }));
4098                            }
4099                        }
4100                        a.clone()
4101                    }).collect::<Vec<_>>()
4102                } else if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena)) {
4103                    // Presto: ARRAY(y) -> ARRAY[y]
4104                    func_args.iter().map(|a| {
4105                        if let Expression::Function(ref f) = a {
4106                            if f.name.to_uppercase() == "ARRAY" && f.args.len() >= 1 {
4107                                return Expression::ArrayFunc(Box::new(crate::expressions::ArrayConstructor {
4108                                    expressions: f.args.clone(),
4109                                    bracket_notation: true,
4110                                    use_list_keyword: false,
4111                                }));
4112                            }
4113                        }
4114                        a.clone()
4115                    }).collect::<Vec<_>>()
4116                } else {
4117                    func_args
4118                };
4119
4120                self.write_keyword("UNNEST");
4121                self.write("(");
4122                for (i, arg) in unnest_args.iter().enumerate() {
4123                    if i > 0 { self.write(", "); }
4124                    self.generate_expression(arg)?;
4125                }
4126                self.write(")");
4127
4128                // POSEXPLODE -> WITH ORDINALITY
4129                if is_posexplode {
4130                    self.write_space();
4131                    self.write_keyword("WITH ORDINALITY");
4132                }
4133            } else {
4134                // Not EXPLODE/POSEXPLODE or not using UNNEST, use LATERAL
4135                if !lv.outer {
4136                    self.write_keyword("LATERAL");
4137                    self.write_space();
4138                }
4139                self.generate_expression(&lv.this)?;
4140            }
4141
4142            // Add table and column aliases
4143            if let Some(alias) = &lv.table_alias {
4144                self.write_space();
4145                self.write_keyword("AS");
4146                self.write_space();
4147                self.generate_identifier(alias)?;
4148                if !lv.column_aliases.is_empty() {
4149                    self.write("(");
4150                    for (i, col) in lv.column_aliases.iter().enumerate() {
4151                        if i > 0 {
4152                            self.write(", ");
4153                        }
4154                        self.generate_identifier(col)?;
4155                    }
4156                    self.write(")");
4157                }
4158            } else if !lv.column_aliases.is_empty() {
4159                // Column aliases without table alias
4160                self.write_space();
4161                self.write_keyword("AS");
4162                self.write(" t(");
4163                for (i, col) in lv.column_aliases.iter().enumerate() {
4164                    if i > 0 {
4165                        self.write(", ");
4166                    }
4167                    self.generate_identifier(col)?;
4168                }
4169                self.write(")");
4170            }
4171
4172            // For LEFT JOIN LATERAL, need ON TRUE
4173            if lv.outer {
4174                self.write_space();
4175                self.write_keyword("ON TRUE");
4176            }
4177        } else {
4178            // Output native LATERAL VIEW syntax (Hive/Spark/Databricks or default)
4179            self.write_keyword("LATERAL VIEW");
4180            if lv.outer {
4181                self.write_space();
4182                self.write_keyword("OUTER");
4183            }
4184            self.write_space();
4185            self.generate_expression(&lv.this)?;
4186
4187            // Table alias
4188            if let Some(alias) = &lv.table_alias {
4189                self.write_space();
4190                self.generate_identifier(alias)?;
4191            }
4192
4193            // Column aliases
4194            if !lv.column_aliases.is_empty() {
4195                self.write_space();
4196                self.write_keyword("AS");
4197                self.write_space();
4198                for (i, col) in lv.column_aliases.iter().enumerate() {
4199                    if i > 0 {
4200                        self.write(", ");
4201                    }
4202                    self.generate_identifier(col)?;
4203                }
4204            }
4205        }
4206
4207        Ok(())
4208    }
4209
4210    fn generate_union(&mut self, union: &Union) -> Result<()> {
4211        // WITH clause
4212        if let Some(with) = &union.with {
4213            self.generate_with(with)?;
4214            self.write_space();
4215        }
4216        self.generate_expression(&union.left)?;
4217        if self.config.pretty {
4218            self.write_newline();
4219            self.write_indent();
4220        } else {
4221            self.write_space();
4222        }
4223
4224        // BigQuery set operation modifiers: [side] [kind] UNION
4225        if let Some(side) = &union.side {
4226            self.write_keyword(side);
4227            self.write_space();
4228        }
4229        if let Some(kind) = &union.kind {
4230            self.write_keyword(kind);
4231            self.write_space();
4232        }
4233
4234        self.write_keyword("UNION");
4235        if union.all {
4236            self.write_space();
4237            self.write_keyword("ALL");
4238        } else if union.distinct {
4239            self.write_space();
4240            self.write_keyword("DISTINCT");
4241        }
4242
4243        // BigQuery: CORRESPONDING/STRICT CORRESPONDING -> BY NAME, BY (cols) -> ON (cols)
4244        // DuckDB: BY NAME
4245        if union.corresponding || union.by_name {
4246            self.write_space();
4247            self.write_keyword("BY NAME");
4248        }
4249        if !union.on_columns.is_empty() {
4250            self.write_space();
4251            self.write_keyword("ON");
4252            self.write(" (");
4253            for (i, col) in union.on_columns.iter().enumerate() {
4254                if i > 0 {
4255                    self.write(", ");
4256                }
4257                self.generate_expression(col)?;
4258            }
4259            self.write(")");
4260        }
4261
4262        if self.config.pretty {
4263            self.write_newline();
4264            self.write_indent();
4265        } else {
4266            self.write_space();
4267        }
4268        self.generate_expression(&union.right)?;
4269        // ORDER BY, LIMIT, OFFSET for the set operation
4270        if let Some(order_by) = &union.order_by {
4271            if self.config.pretty {
4272                self.write_newline();
4273            } else {
4274                self.write_space();
4275            }
4276            self.write_keyword("ORDER BY");
4277            self.write_space();
4278            for (i, ordered) in order_by.expressions.iter().enumerate() {
4279                if i > 0 {
4280                    self.write(", ");
4281                }
4282                self.generate_ordered(ordered)?;
4283            }
4284        }
4285        if let Some(limit) = &union.limit {
4286            if self.config.pretty {
4287                self.write_newline();
4288            } else {
4289                self.write_space();
4290            }
4291            self.write_keyword("LIMIT");
4292            self.write_space();
4293            self.generate_expression(limit)?;
4294        }
4295        if let Some(offset) = &union.offset {
4296            if self.config.pretty {
4297                self.write_newline();
4298            } else {
4299                self.write_space();
4300            }
4301            self.write_keyword("OFFSET");
4302            self.write_space();
4303            self.generate_expression(offset)?;
4304        }
4305        // DISTRIBUTE BY (Hive/Spark)
4306        if let Some(distribute_by) = &union.distribute_by {
4307            self.write_space();
4308            self.write_keyword("DISTRIBUTE BY");
4309            self.write_space();
4310            for (i, expr) in distribute_by.expressions.iter().enumerate() {
4311                if i > 0 {
4312                    self.write(", ");
4313                }
4314                self.generate_expression(expr)?;
4315            }
4316        }
4317        // SORT BY (Hive/Spark)
4318        if let Some(sort_by) = &union.sort_by {
4319            self.write_space();
4320            self.write_keyword("SORT BY");
4321            self.write_space();
4322            for (i, ord) in sort_by.expressions.iter().enumerate() {
4323                if i > 0 {
4324                    self.write(", ");
4325                }
4326                self.generate_ordered(ord)?;
4327            }
4328        }
4329        // CLUSTER BY (Hive/Spark)
4330        if let Some(cluster_by) = &union.cluster_by {
4331            self.write_space();
4332            self.write_keyword("CLUSTER BY");
4333            self.write_space();
4334            for (i, ord) in cluster_by.expressions.iter().enumerate() {
4335                if i > 0 {
4336                    self.write(", ");
4337                }
4338                self.generate_ordered(ord)?;
4339            }
4340        }
4341        Ok(())
4342    }
4343
4344    fn generate_intersect(&mut self, intersect: &Intersect) -> Result<()> {
4345        // WITH clause
4346        if let Some(with) = &intersect.with {
4347            self.generate_with(with)?;
4348            self.write_space();
4349        }
4350        self.generate_expression(&intersect.left)?;
4351        if self.config.pretty {
4352            self.write_newline();
4353            self.write_indent();
4354        } else {
4355            self.write_space();
4356        }
4357
4358        // BigQuery set operation modifiers: [side] [kind] INTERSECT
4359        if let Some(side) = &intersect.side {
4360            self.write_keyword(side);
4361            self.write_space();
4362        }
4363        if let Some(kind) = &intersect.kind {
4364            self.write_keyword(kind);
4365            self.write_space();
4366        }
4367
4368        self.write_keyword("INTERSECT");
4369        if intersect.all {
4370            self.write_space();
4371            self.write_keyword("ALL");
4372        } else if intersect.distinct {
4373            self.write_space();
4374            self.write_keyword("DISTINCT");
4375        }
4376
4377        // BigQuery: CORRESPONDING/STRICT CORRESPONDING -> BY NAME, BY (cols) -> ON (cols)
4378        // DuckDB: BY NAME
4379        if intersect.corresponding || intersect.by_name {
4380            self.write_space();
4381            self.write_keyword("BY NAME");
4382        }
4383        if !intersect.on_columns.is_empty() {
4384            self.write_space();
4385            self.write_keyword("ON");
4386            self.write(" (");
4387            for (i, col) in intersect.on_columns.iter().enumerate() {
4388                if i > 0 {
4389                    self.write(", ");
4390                }
4391                self.generate_expression(col)?;
4392            }
4393            self.write(")");
4394        }
4395
4396        if self.config.pretty {
4397            self.write_newline();
4398            self.write_indent();
4399        } else {
4400            self.write_space();
4401        }
4402        self.generate_expression(&intersect.right)?;
4403        // ORDER BY, LIMIT, OFFSET for the set operation
4404        if let Some(order_by) = &intersect.order_by {
4405            if self.config.pretty {
4406                self.write_newline();
4407            } else {
4408                self.write_space();
4409            }
4410            self.write_keyword("ORDER BY");
4411            self.write_space();
4412            for (i, ordered) in order_by.expressions.iter().enumerate() {
4413                if i > 0 {
4414                    self.write(", ");
4415                }
4416                self.generate_ordered(ordered)?;
4417            }
4418        }
4419        if let Some(limit) = &intersect.limit {
4420            if self.config.pretty {
4421                self.write_newline();
4422            } else {
4423                self.write_space();
4424            }
4425            self.write_keyword("LIMIT");
4426            self.write_space();
4427            self.generate_expression(limit)?;
4428        }
4429        if let Some(offset) = &intersect.offset {
4430            if self.config.pretty {
4431                self.write_newline();
4432            } else {
4433                self.write_space();
4434            }
4435            self.write_keyword("OFFSET");
4436            self.write_space();
4437            self.generate_expression(offset)?;
4438        }
4439        // DISTRIBUTE BY (Hive/Spark)
4440        if let Some(distribute_by) = &intersect.distribute_by {
4441            self.write_space();
4442            self.write_keyword("DISTRIBUTE BY");
4443            self.write_space();
4444            for (i, expr) in distribute_by.expressions.iter().enumerate() {
4445                if i > 0 {
4446                    self.write(", ");
4447                }
4448                self.generate_expression(expr)?;
4449            }
4450        }
4451        // SORT BY (Hive/Spark)
4452        if let Some(sort_by) = &intersect.sort_by {
4453            self.write_space();
4454            self.write_keyword("SORT BY");
4455            self.write_space();
4456            for (i, ord) in sort_by.expressions.iter().enumerate() {
4457                if i > 0 {
4458                    self.write(", ");
4459                }
4460                self.generate_ordered(ord)?;
4461            }
4462        }
4463        // CLUSTER BY (Hive/Spark)
4464        if let Some(cluster_by) = &intersect.cluster_by {
4465            self.write_space();
4466            self.write_keyword("CLUSTER BY");
4467            self.write_space();
4468            for (i, ord) in cluster_by.expressions.iter().enumerate() {
4469                if i > 0 {
4470                    self.write(", ");
4471                }
4472                self.generate_ordered(ord)?;
4473            }
4474        }
4475        Ok(())
4476    }
4477
4478    fn generate_except(&mut self, except: &Except) -> Result<()> {
4479        use crate::dialects::DialectType;
4480
4481        // WITH clause
4482        if let Some(with) = &except.with {
4483            self.generate_with(with)?;
4484            self.write_space();
4485        }
4486
4487        self.generate_expression(&except.left)?;
4488        if self.config.pretty {
4489            self.write_newline();
4490            self.write_indent();
4491        } else {
4492            self.write_space();
4493        }
4494
4495        // BigQuery set operation modifiers: [side] [kind] EXCEPT
4496        if let Some(side) = &except.side {
4497            self.write_keyword(side);
4498            self.write_space();
4499        }
4500        if let Some(kind) = &except.kind {
4501            self.write_keyword(kind);
4502            self.write_space();
4503        }
4504
4505        // Oracle uses MINUS instead of EXCEPT
4506        match self.config.dialect {
4507            Some(DialectType::Oracle) => {
4508                self.write_keyword("MINUS");
4509                // Note: Oracle MINUS doesn't support ALL
4510            }
4511            _ => {
4512                self.write_keyword("EXCEPT");
4513                if except.all {
4514                    self.write_space();
4515                    self.write_keyword("ALL");
4516                } else if except.distinct {
4517                    self.write_space();
4518                    self.write_keyword("DISTINCT");
4519                }
4520            }
4521        }
4522
4523        // BigQuery: CORRESPONDING/STRICT CORRESPONDING -> BY NAME, BY (cols) -> ON (cols)
4524        // DuckDB: BY NAME
4525        if except.corresponding || except.by_name {
4526            self.write_space();
4527            self.write_keyword("BY NAME");
4528        }
4529        if !except.on_columns.is_empty() {
4530            self.write_space();
4531            self.write_keyword("ON");
4532            self.write(" (");
4533            for (i, col) in except.on_columns.iter().enumerate() {
4534                if i > 0 {
4535                    self.write(", ");
4536                }
4537                self.generate_expression(col)?;
4538            }
4539            self.write(")");
4540        }
4541
4542        if self.config.pretty {
4543            self.write_newline();
4544            self.write_indent();
4545        } else {
4546            self.write_space();
4547        }
4548        self.generate_expression(&except.right)?;
4549        // ORDER BY, LIMIT, OFFSET for the set operation
4550        if let Some(order_by) = &except.order_by {
4551            if self.config.pretty {
4552                self.write_newline();
4553            } else {
4554                self.write_space();
4555            }
4556            self.write_keyword("ORDER BY");
4557            self.write_space();
4558            for (i, ordered) in order_by.expressions.iter().enumerate() {
4559                if i > 0 {
4560                    self.write(", ");
4561                }
4562                self.generate_ordered(ordered)?;
4563            }
4564        }
4565        if let Some(limit) = &except.limit {
4566            if self.config.pretty {
4567                self.write_newline();
4568            } else {
4569                self.write_space();
4570            }
4571            self.write_keyword("LIMIT");
4572            self.write_space();
4573            self.generate_expression(limit)?;
4574        }
4575        if let Some(offset) = &except.offset {
4576            if self.config.pretty {
4577                self.write_newline();
4578            } else {
4579                self.write_space();
4580            }
4581            self.write_keyword("OFFSET");
4582            self.write_space();
4583            self.generate_expression(offset)?;
4584        }
4585        // DISTRIBUTE BY (Hive/Spark)
4586        if let Some(distribute_by) = &except.distribute_by {
4587            self.write_space();
4588            self.write_keyword("DISTRIBUTE BY");
4589            self.write_space();
4590            for (i, expr) in distribute_by.expressions.iter().enumerate() {
4591                if i > 0 {
4592                    self.write(", ");
4593                }
4594                self.generate_expression(expr)?;
4595            }
4596        }
4597        // SORT BY (Hive/Spark)
4598        if let Some(sort_by) = &except.sort_by {
4599            self.write_space();
4600            self.write_keyword("SORT BY");
4601            self.write_space();
4602            for (i, ord) in sort_by.expressions.iter().enumerate() {
4603                if i > 0 {
4604                    self.write(", ");
4605                }
4606                self.generate_ordered(ord)?;
4607            }
4608        }
4609        // CLUSTER BY (Hive/Spark)
4610        if let Some(cluster_by) = &except.cluster_by {
4611            self.write_space();
4612            self.write_keyword("CLUSTER BY");
4613            self.write_space();
4614            for (i, ord) in cluster_by.expressions.iter().enumerate() {
4615                if i > 0 {
4616                    self.write(", ");
4617                }
4618                self.generate_ordered(ord)?;
4619            }
4620        }
4621        Ok(())
4622    }
4623
4624    fn generate_insert(&mut self, insert: &Insert) -> Result<()> {
4625        // For TSQL/Fabric/Spark/Hive/Databricks, CTEs must be prepended before INSERT
4626        let prepend_query_cte = if insert.with.is_none() {
4627            use crate::dialects::DialectType;
4628            let should_prepend = matches!(self.config.dialect,
4629                Some(DialectType::TSQL) | Some(DialectType::Fabric)
4630                | Some(DialectType::Spark)
4631                | Some(DialectType::Databricks) | Some(DialectType::Hive)
4632            );
4633            if should_prepend {
4634                if let Some(Expression::Select(select)) = &insert.query {
4635                    select.with.clone()
4636                } else {
4637                    None
4638                }
4639            } else {
4640                None
4641            }
4642        } else {
4643            None
4644        };
4645
4646        // Output WITH clause if on INSERT (e.g., WITH ... INSERT INTO ...)
4647        if let Some(with) = &insert.with {
4648            self.generate_with(with)?;
4649            self.write_space();
4650        } else if let Some(with) = &prepend_query_cte {
4651            self.generate_with(with)?;
4652            self.write_space();
4653        }
4654
4655        // Output leading comments before INSERT
4656        for comment in &insert.leading_comments {
4657            self.write(comment);
4658            self.write(" ");
4659        }
4660
4661        // Handle directory insert (INSERT OVERWRITE DIRECTORY)
4662        if let Some(dir) = &insert.directory {
4663            self.write_keyword("INSERT OVERWRITE");
4664            if dir.local {
4665                self.write_space();
4666                self.write_keyword("LOCAL");
4667            }
4668            self.write_space();
4669            self.write_keyword("DIRECTORY");
4670            self.write_space();
4671            self.write("'");
4672            self.write(&dir.path);
4673            self.write("'");
4674
4675            // ROW FORMAT clause
4676            if let Some(row_format) = &dir.row_format {
4677                self.write_space();
4678                self.write_keyword("ROW FORMAT");
4679                if row_format.delimited {
4680                    self.write_space();
4681                    self.write_keyword("DELIMITED");
4682                }
4683                if let Some(val) = &row_format.fields_terminated_by {
4684                    self.write_space();
4685                    self.write_keyword("FIELDS TERMINATED BY");
4686                    self.write_space();
4687                    self.write("'");
4688                    self.write(val);
4689                    self.write("'");
4690                }
4691                if let Some(val) = &row_format.collection_items_terminated_by {
4692                    self.write_space();
4693                    self.write_keyword("COLLECTION ITEMS TERMINATED BY");
4694                    self.write_space();
4695                    self.write("'");
4696                    self.write(val);
4697                    self.write("'");
4698                }
4699                if let Some(val) = &row_format.map_keys_terminated_by {
4700                    self.write_space();
4701                    self.write_keyword("MAP KEYS TERMINATED BY");
4702                    self.write_space();
4703                    self.write("'");
4704                    self.write(val);
4705                    self.write("'");
4706                }
4707                if let Some(val) = &row_format.lines_terminated_by {
4708                    self.write_space();
4709                    self.write_keyword("LINES TERMINATED BY");
4710                    self.write_space();
4711                    self.write("'");
4712                    self.write(val);
4713                    self.write("'");
4714                }
4715                if let Some(val) = &row_format.null_defined_as {
4716                    self.write_space();
4717                    self.write_keyword("NULL DEFINED AS");
4718                    self.write_space();
4719                    self.write("'");
4720                    self.write(val);
4721                    self.write("'");
4722                }
4723            }
4724
4725            // STORED AS clause
4726            if let Some(format) = &dir.stored_as {
4727                self.write_space();
4728                self.write_keyword("STORED AS");
4729                self.write_space();
4730                self.write_keyword(format);
4731            }
4732
4733            // Query (SELECT statement)
4734            if let Some(query) = &insert.query {
4735                self.write_space();
4736                self.generate_expression(query)?;
4737            }
4738
4739            return Ok(());
4740        }
4741
4742        if insert.is_replace {
4743            // MySQL/SQLite REPLACE INTO statement
4744            self.write_keyword("REPLACE INTO");
4745        } else if insert.overwrite {
4746            // Use dialect-specific INSERT OVERWRITE format
4747            self.write_keyword("INSERT");
4748            // Output hint if present (Oracle: INSERT /*+ APPEND */ INTO)
4749            if let Some(ref hint) = insert.hint {
4750                self.generate_hint(hint)?;
4751            }
4752            self.write(&self.config.insert_overwrite.to_uppercase());
4753        } else if let Some(ref action) = insert.conflict_action {
4754            // SQLite conflict action: INSERT OR ABORT|FAIL|IGNORE|REPLACE|ROLLBACK INTO
4755            self.write_keyword("INSERT OR");
4756            self.write_space();
4757            self.write_keyword(action);
4758            self.write_space();
4759            self.write_keyword("INTO");
4760        } else if insert.ignore {
4761            // MySQL INSERT IGNORE syntax
4762            self.write_keyword("INSERT IGNORE INTO");
4763        } else {
4764            self.write_keyword("INSERT");
4765            // Output hint if present (Oracle: INSERT /*+ APPEND */ INTO)
4766            if let Some(ref hint) = insert.hint {
4767                self.generate_hint(hint)?;
4768            }
4769            self.write_space();
4770            self.write_keyword("INTO");
4771        }
4772        // ClickHouse: INSERT INTO FUNCTION func_name(args...)
4773        if let Some(ref func) = insert.function_target {
4774            self.write_space();
4775            self.write_keyword("FUNCTION");
4776            self.write_space();
4777            self.generate_expression(func)?;
4778        } else {
4779            self.write_space();
4780            self.generate_table(&insert.table)?;
4781        }
4782
4783        // Table alias (PostgreSQL: INSERT INTO table AS t(...), Oracle: INSERT INTO table t ...)
4784        if let Some(ref alias) = insert.alias {
4785            self.write_space();
4786            if insert.alias_explicit_as {
4787                self.write_keyword("AS");
4788                self.write_space();
4789            }
4790            self.generate_identifier(alias)?;
4791        }
4792
4793        // IF EXISTS clause (Hive)
4794        if insert.if_exists {
4795            self.write_space();
4796            self.write_keyword("IF EXISTS");
4797        }
4798
4799        // REPLACE WHERE clause (Databricks)
4800        if let Some(ref replace_where) = insert.replace_where {
4801            self.write_space();
4802            self.write_keyword("REPLACE WHERE");
4803            self.write_space();
4804            self.generate_expression(replace_where)?;
4805        }
4806
4807        // Generate PARTITION clause if present
4808        if !insert.partition.is_empty() {
4809            self.write_space();
4810            self.write_keyword("PARTITION");
4811            self.write("(");
4812            for (i, (col, val)) in insert.partition.iter().enumerate() {
4813                if i > 0 {
4814                    self.write(", ");
4815                }
4816                self.generate_identifier(col)?;
4817                if let Some(v) = val {
4818                    self.write(" = ");
4819                    self.generate_expression(v)?;
4820                }
4821            }
4822            self.write(")");
4823        }
4824
4825        // ClickHouse: PARTITION BY expr
4826        if let Some(ref partition_by) = insert.partition_by {
4827            self.write_space();
4828            self.write_keyword("PARTITION BY");
4829            self.write_space();
4830            self.generate_expression(partition_by)?;
4831        }
4832
4833        // ClickHouse: SETTINGS key = val, ...
4834        if !insert.settings.is_empty() {
4835            self.write_space();
4836            self.write_keyword("SETTINGS");
4837            self.write_space();
4838            for (i, setting) in insert.settings.iter().enumerate() {
4839                if i > 0 {
4840                    self.write(", ");
4841                }
4842                self.generate_expression(setting)?;
4843            }
4844        }
4845
4846        if !insert.columns.is_empty() {
4847            if insert.alias.is_some() && insert.alias_explicit_as {
4848                // No space when explicit AS alias is present: INSERT INTO table AS t(a, b, c)
4849                self.write("(");
4850            } else {
4851                // Space for implicit alias or no alias: INSERT INTO dest d (i, value)
4852                self.write(" (");
4853            }
4854            for (i, col) in insert.columns.iter().enumerate() {
4855                if i > 0 {
4856                    self.write(", ");
4857                }
4858                self.generate_identifier(col)?;
4859            }
4860            self.write(")");
4861        }
4862
4863        // OUTPUT clause (TSQL)
4864        if let Some(ref output) = insert.output {
4865            self.generate_output_clause(output)?;
4866        }
4867
4868        // BY NAME modifier (DuckDB)
4869        if insert.by_name {
4870            self.write_space();
4871            self.write_keyword("BY NAME");
4872        }
4873
4874        if insert.default_values {
4875            self.write_space();
4876            self.write_keyword("DEFAULT VALUES");
4877        } else if let Some(query) = &insert.query {
4878            if self.config.pretty {
4879                self.write_newline();
4880            } else {
4881                self.write_space();
4882            }
4883            // If we prepended CTEs from nested SELECT (TSQL), strip the WITH from SELECT
4884            if prepend_query_cte.is_some() {
4885                if let Expression::Select(select) = query {
4886                    let mut select_no_with = select.clone();
4887                    select_no_with.with = None;
4888                    self.generate_select(&select_no_with)?;
4889                } else {
4890                    self.generate_expression(query)?;
4891                }
4892            } else {
4893                self.generate_expression(query)?;
4894            }
4895        } else if !insert.values.is_empty() {
4896            if self.config.pretty {
4897                // Pretty printing: VALUES on new line, each tuple indented
4898                self.write_newline();
4899                self.write_keyword("VALUES");
4900                self.write_newline();
4901                self.indent_level += 1;
4902                for (i, row) in insert.values.iter().enumerate() {
4903                    if i > 0 {
4904                        self.write(",");
4905                        self.write_newline();
4906                    }
4907                    self.write_indent();
4908                    self.write("(");
4909                    for (j, val) in row.iter().enumerate() {
4910                        if j > 0 {
4911                            self.write(", ");
4912                        }
4913                        self.generate_expression(val)?;
4914                    }
4915                    self.write(")");
4916                }
4917                self.indent_level -= 1;
4918            } else {
4919                // Non-pretty: single line
4920                self.write_space();
4921                self.write_keyword("VALUES");
4922                for (i, row) in insert.values.iter().enumerate() {
4923                    if i > 0 {
4924                        self.write(",");
4925                    }
4926                    self.write(" (");
4927                    for (j, val) in row.iter().enumerate() {
4928                        if j > 0 {
4929                            self.write(", ");
4930                        }
4931                        self.generate_expression(val)?;
4932                    }
4933                    self.write(")");
4934                }
4935            }
4936        }
4937
4938        // Source table (Hive/Spark): INSERT OVERWRITE TABLE target TABLE source
4939        if let Some(ref source) = insert.source {
4940            self.write_space();
4941            self.write_keyword("TABLE");
4942            self.write_space();
4943            self.generate_expression(source)?;
4944        }
4945
4946        // Source alias (MySQL: VALUES (...) AS new_data)
4947        if let Some(alias) = &insert.source_alias {
4948            self.write_space();
4949            self.write_keyword("AS");
4950            self.write_space();
4951            self.generate_identifier(alias)?;
4952        }
4953
4954        // ON CONFLICT clause (Materialize doesn't support ON CONFLICT)
4955        if let Some(on_conflict) = &insert.on_conflict {
4956            if !matches!(self.config.dialect, Some(DialectType::Materialize)) {
4957                self.write_space();
4958                self.generate_expression(on_conflict)?;
4959            }
4960        }
4961
4962        // RETURNING clause
4963        if !insert.returning.is_empty() {
4964            self.write_space();
4965            self.write_keyword("RETURNING");
4966            self.write_space();
4967            for (i, expr) in insert.returning.iter().enumerate() {
4968                if i > 0 {
4969                    self.write(", ");
4970                }
4971                self.generate_expression(expr)?;
4972            }
4973        }
4974
4975        Ok(())
4976    }
4977
4978    fn generate_update(&mut self, update: &Update) -> Result<()> {
4979        // Output leading comments before UPDATE
4980        for comment in &update.leading_comments {
4981            self.write(comment);
4982            self.write(" ");
4983        }
4984
4985        // WITH clause (CTEs)
4986        if let Some(ref with) = update.with {
4987            self.generate_with(with)?;
4988            self.write_space();
4989        }
4990
4991        self.write_keyword("UPDATE");
4992        self.write_space();
4993        self.generate_table(&update.table)?;
4994
4995        let mysql_like_update_from = matches!(
4996            self.config.dialect,
4997            Some(DialectType::MySQL) | Some(DialectType::SingleStore)
4998        ) && update.from_clause.is_some();
4999
5000        let mut set_pairs = update.set.clone();
5001
5002        // MySQL-style UPDATE doesn't support FROM after SET. Convert FROM tables to JOIN ... ON TRUE.
5003        let mut pre_set_joins = update.table_joins.clone();
5004        if mysql_like_update_from {
5005            let target_name = update.table.alias
5006                .as_ref()
5007                .map(|a| a.name.clone())
5008                .unwrap_or_else(|| update.table.name.name.clone());
5009
5010            for (col, _) in &mut set_pairs {
5011                if !col.name.contains('.') {
5012                    col.name = format!("{}.{}", target_name, col.name);
5013                }
5014            }
5015
5016            if let Some(from_clause) = &update.from_clause {
5017                for table_expr in &from_clause.expressions {
5018                    pre_set_joins.push(crate::expressions::Join {
5019                        this: table_expr.clone(),
5020                        on: Some(Expression::Boolean(crate::expressions::BooleanLiteral { value: true })),
5021                        using: Vec::new(),
5022                        kind: crate::expressions::JoinKind::Inner,
5023                        use_inner_keyword: false,
5024                        use_outer_keyword: false,
5025                        deferred_condition: false,
5026                        join_hint: None,
5027                        match_condition: None,
5028                        pivots: Vec::new(),
5029                    });
5030                }
5031            }
5032            for join in &update.from_joins {
5033                let mut join = join.clone();
5034                if join.on.is_none() && join.using.is_empty() {
5035                    join.on = Some(Expression::Boolean(crate::expressions::BooleanLiteral { value: true }));
5036                }
5037                pre_set_joins.push(join);
5038            }
5039        }
5040
5041        // Extra tables for multi-table UPDATE (MySQL syntax)
5042        for extra_table in &update.extra_tables {
5043            self.write(", ");
5044            self.generate_table(extra_table)?;
5045        }
5046
5047        // JOINs attached to the table list (MySQL multi-table syntax)
5048        for join in &pre_set_joins {
5049            // generate_join already adds a leading space
5050            self.generate_join(join)?;
5051        }
5052
5053        // Teradata: FROM clause comes before SET
5054        let teradata_from_before_set = matches!(self.config.dialect, Some(DialectType::Teradata));
5055        if teradata_from_before_set && !mysql_like_update_from {
5056            if let Some(ref from_clause) = update.from_clause {
5057                self.write_space();
5058                self.write_keyword("FROM");
5059                self.write_space();
5060                for (i, table_expr) in from_clause.expressions.iter().enumerate() {
5061                    if i > 0 {
5062                        self.write(", ");
5063                    }
5064                    self.generate_expression(table_expr)?;
5065                }
5066            }
5067            for join in &update.from_joins {
5068                self.generate_join(join)?;
5069            }
5070        }
5071
5072        self.write_space();
5073        self.write_keyword("SET");
5074        self.write_space();
5075
5076        for (i, (col, val)) in set_pairs.iter().enumerate() {
5077            if i > 0 {
5078                self.write(", ");
5079            }
5080            self.generate_identifier(col)?;
5081            self.write(" = ");
5082            self.generate_expression(val)?;
5083        }
5084
5085        // OUTPUT clause (TSQL)
5086        if let Some(ref output) = update.output {
5087            self.generate_output_clause(output)?;
5088        }
5089
5090        // FROM clause (after SET for non-Teradata, non-MySQL dialects)
5091        if !mysql_like_update_from && !teradata_from_before_set {
5092            if let Some(ref from_clause) = update.from_clause {
5093                self.write_space();
5094                self.write_keyword("FROM");
5095                self.write_space();
5096                // Generate each table in the FROM clause
5097                for (i, table_expr) in from_clause.expressions.iter().enumerate() {
5098                    if i > 0 {
5099                        self.write(", ");
5100                    }
5101                    self.generate_expression(table_expr)?;
5102                }
5103            }
5104        }
5105
5106        if !mysql_like_update_from && !teradata_from_before_set {
5107            // JOINs after FROM clause (PostgreSQL, Snowflake, SQL Server syntax)
5108            for join in &update.from_joins {
5109                self.generate_join(join)?;
5110            }
5111        }
5112
5113        if let Some(where_clause) = &update.where_clause {
5114            self.write_space();
5115            self.write_keyword("WHERE");
5116            self.write_space();
5117            self.generate_expression(&where_clause.this)?;
5118        }
5119
5120        // RETURNING clause
5121        if !update.returning.is_empty() {
5122            self.write_space();
5123            self.write_keyword("RETURNING");
5124            self.write_space();
5125            for (i, expr) in update.returning.iter().enumerate() {
5126                if i > 0 {
5127                    self.write(", ");
5128                }
5129                self.generate_expression(expr)?;
5130            }
5131        }
5132
5133        // ORDER BY clause (MySQL)
5134        if let Some(ref order_by) = update.order_by {
5135            self.write_space();
5136            self.generate_order_by(order_by)?;
5137        }
5138
5139        // LIMIT clause (MySQL)
5140        if let Some(ref limit) = update.limit {
5141            self.write_space();
5142            self.write_keyword("LIMIT");
5143            self.write_space();
5144            self.generate_expression(limit)?;
5145        }
5146
5147        Ok(())
5148    }
5149
5150    fn generate_delete(&mut self, delete: &Delete) -> Result<()> {
5151        // Output WITH clause if present
5152        if let Some(with) = &delete.with {
5153            self.generate_with(with)?;
5154            self.write_space();
5155        }
5156
5157        // Output leading comments before DELETE
5158        for comment in &delete.leading_comments {
5159            self.write(comment);
5160            self.write(" ");
5161        }
5162
5163        // MySQL multi-table DELETE or TSQL DELETE with OUTPUT before FROM
5164        if !delete.tables.is_empty() && !delete.tables_from_using {
5165            // DELETE t1[, t2] [OUTPUT ...] FROM ... syntax (tables before FROM)
5166            self.write_keyword("DELETE");
5167            self.write_space();
5168            for (i, tbl) in delete.tables.iter().enumerate() {
5169                if i > 0 {
5170                    self.write(", ");
5171                }
5172                self.generate_table(tbl)?;
5173            }
5174            // TSQL: OUTPUT clause between target table and FROM
5175            if let Some(ref output) = delete.output {
5176                self.generate_output_clause(output)?;
5177            }
5178            self.write_space();
5179            self.write_keyword("FROM");
5180            self.write_space();
5181            self.generate_table(&delete.table)?;
5182        } else if !delete.tables.is_empty() && delete.tables_from_using {
5183            // DELETE FROM t1, t2 USING ... syntax (tables after FROM)
5184            self.write_keyword("DELETE FROM");
5185            self.write_space();
5186            for (i, tbl) in delete.tables.iter().enumerate() {
5187                if i > 0 {
5188                    self.write(", ");
5189                }
5190                self.generate_table(tbl)?;
5191            }
5192        } else if delete.no_from && matches!(self.config.dialect, Some(DialectType::BigQuery)) {
5193            // BigQuery-style DELETE without FROM keyword
5194            self.write_keyword("DELETE");
5195            self.write_space();
5196            self.generate_table(&delete.table)?;
5197        } else {
5198            self.write_keyword("DELETE FROM");
5199            self.write_space();
5200            self.generate_table(&delete.table)?;
5201        }
5202
5203        // ClickHouse: ON CLUSTER clause
5204        if let Some(ref on_cluster) = delete.on_cluster {
5205            self.write_space();
5206            self.generate_on_cluster(on_cluster)?;
5207        }
5208
5209        // FORCE INDEX hint (MySQL)
5210        if let Some(ref idx) = delete.force_index {
5211            self.write_space();
5212            self.write_keyword("FORCE INDEX");
5213            self.write(" (");
5214            self.write(idx);
5215            self.write(")");
5216        }
5217
5218        // Optional alias
5219        if let Some(ref alias) = delete.alias {
5220            self.write_space();
5221            if delete.alias_explicit_as || matches!(self.config.dialect, Some(DialectType::BigQuery)) {
5222                self.write_keyword("AS");
5223                self.write_space();
5224            }
5225            self.generate_identifier(alias)?;
5226        }
5227
5228        // JOINs (MySQL multi-table) - when NOT tables_from_using, JOINs come before USING
5229        if !delete.tables_from_using {
5230            for join in &delete.joins {
5231                self.generate_join(join)?;
5232            }
5233        }
5234
5235        // USING clause (PostgreSQL/DuckDB/MySQL)
5236        if !delete.using.is_empty() {
5237            self.write_space();
5238            self.write_keyword("USING");
5239            for (i, table) in delete.using.iter().enumerate() {
5240                if i > 0 {
5241                    self.write(",");
5242                }
5243                self.write_space();
5244                // Check if the table has subquery hints (DuckDB USING with subquery)
5245                if !table.hints.is_empty() && table.name.is_empty() {
5246                    // Subquery in USING: (VALUES ...) AS alias(cols)
5247                    self.generate_expression(&table.hints[0])?;
5248                    if let Some(ref alias) = table.alias {
5249                        self.write_space();
5250                        if table.alias_explicit_as {
5251                            self.write_keyword("AS");
5252                            self.write_space();
5253                        }
5254                        self.generate_identifier(alias)?;
5255                        if !table.column_aliases.is_empty() {
5256                            self.write("(");
5257                            for (j, col_alias) in table.column_aliases.iter().enumerate() {
5258                                if j > 0 {
5259                                    self.write(", ");
5260                                }
5261                                self.generate_identifier(col_alias)?;
5262                            }
5263                            self.write(")");
5264                        }
5265                    }
5266                } else {
5267                    self.generate_table(table)?;
5268                }
5269            }
5270        }
5271
5272        // JOINs (MySQL multi-table) - when tables_from_using, JOINs come after USING
5273        if delete.tables_from_using {
5274            for join in &delete.joins {
5275                self.generate_join(join)?;
5276            }
5277        }
5278
5279        // OUTPUT clause (TSQL) - only if not already emitted in the early position
5280        let output_already_emitted = !delete.tables.is_empty() && !delete.tables_from_using && delete.output.is_some();
5281        if !output_already_emitted {
5282            if let Some(ref output) = delete.output {
5283                self.generate_output_clause(output)?;
5284            }
5285        }
5286
5287        if let Some(where_clause) = &delete.where_clause {
5288            self.write_space();
5289            self.write_keyword("WHERE");
5290            self.write_space();
5291            self.generate_expression(&where_clause.this)?;
5292        }
5293
5294        // ORDER BY clause (MySQL)
5295        if let Some(ref order_by) = delete.order_by {
5296            self.write_space();
5297            self.generate_order_by(order_by)?;
5298        }
5299
5300        // LIMIT clause (MySQL)
5301        if let Some(ref limit) = delete.limit {
5302            self.write_space();
5303            self.write_keyword("LIMIT");
5304            self.write_space();
5305            self.generate_expression(limit)?;
5306        }
5307
5308        // RETURNING clause (PostgreSQL)
5309        if !delete.returning.is_empty() {
5310            self.write_space();
5311            self.write_keyword("RETURNING");
5312            self.write_space();
5313            for (i, expr) in delete.returning.iter().enumerate() {
5314                if i > 0 {
5315                    self.write(", ");
5316                }
5317                self.generate_expression(expr)?;
5318            }
5319        }
5320
5321        Ok(())
5322    }
5323
5324    // ==================== DDL Generation ====================
5325
5326    fn generate_create_table(&mut self, ct: &CreateTable) -> Result<()> {
5327        // Athena: Determine if this is Hive-style DDL or Trino-style DML
5328        // CREATE TABLE AS SELECT uses Trino (double quotes)
5329        // CREATE TABLE (without AS SELECT) and CREATE EXTERNAL TABLE use Hive (backticks)
5330        let saved_athena_hive_context = self.athena_hive_context;
5331        let is_clickhouse = matches!(self.config.dialect, Some(DialectType::ClickHouse));
5332        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
5333            // Use Hive context if:
5334            // 1. It's an EXTERNAL table, OR
5335            // 2. There's no AS SELECT clause
5336            let is_external = ct.table_modifier.as_ref().map(|m| m.eq_ignore_ascii_case("EXTERNAL")).unwrap_or(false);
5337            let has_as_select = ct.as_select.is_some();
5338            self.athena_hive_context = is_external || !has_as_select;
5339        }
5340
5341        // TSQL: Convert CREATE TABLE AS SELECT to SELECT * INTO table FROM (subquery) AS temp
5342        if matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL)) {
5343            if let Some(ref query) = ct.as_select {
5344                // Output WITH CTE clause if present
5345                if let Some(with_cte) = &ct.with_cte {
5346                    self.generate_with(with_cte)?;
5347                    self.write_space();
5348                }
5349
5350                // Generate: SELECT * INTO [table] FROM (subquery) AS temp
5351                self.write_keyword("SELECT");
5352                self.write(" * ");
5353                self.write_keyword("INTO");
5354                self.write_space();
5355
5356                // If temporary, prefix with # for TSQL temp table
5357                if ct.temporary {
5358                    self.write("#");
5359                }
5360                self.generate_table(&ct.name)?;
5361
5362                self.write_space();
5363                self.write_keyword("FROM");
5364                self.write(" (");
5365                // For TSQL, add aliases to select columns to preserve column names
5366                let aliased_query = Self::add_column_aliases_to_query(query.clone());
5367                self.generate_expression(&aliased_query)?;
5368                self.write(") ");
5369                self.write_keyword("AS");
5370                self.write(" temp");
5371                return Ok(());
5372            }
5373        }
5374
5375        // Output WITH CTE clause if present
5376        if let Some(with_cte) = &ct.with_cte {
5377            self.generate_with(with_cte)?;
5378            self.write_space();
5379        }
5380
5381        // Output leading comments before CREATE
5382        for comment in &ct.leading_comments {
5383            self.write(comment);
5384            self.write(" ");
5385        }
5386        self.write_keyword("CREATE");
5387
5388        if ct.or_replace {
5389            self.write_space();
5390            self.write_keyword("OR REPLACE");
5391        }
5392
5393        if ct.temporary {
5394            self.write_space();
5395            // Oracle uses GLOBAL TEMPORARY TABLE syntax
5396            if matches!(self.config.dialect, Some(DialectType::Oracle)) {
5397                self.write_keyword("GLOBAL TEMPORARY");
5398            } else {
5399                self.write_keyword("TEMPORARY");
5400            }
5401        }
5402
5403        // Table modifier: DYNAMIC, ICEBERG, EXTERNAL, HYBRID, TRANSIENT
5404        let is_dictionary = ct
5405            .table_modifier
5406            .as_ref()
5407            .map(|m| m.eq_ignore_ascii_case("DICTIONARY"))
5408            .unwrap_or(false);
5409        if let Some(ref modifier) = ct.table_modifier {
5410            // TRANSIENT is Snowflake-specific - skip for other dialects
5411            let skip_transient = modifier.eq_ignore_ascii_case("TRANSIENT")
5412                && !matches!(self.config.dialect, Some(DialectType::Snowflake) | None);
5413            // Teradata-specific modifiers: VOLATILE, SET, MULTISET, SET TABLE combinations
5414            let is_teradata_modifier = modifier.eq_ignore_ascii_case("VOLATILE")
5415                || modifier.eq_ignore_ascii_case("SET")
5416                || modifier.eq_ignore_ascii_case("MULTISET")
5417                || modifier.to_uppercase().contains("VOLATILE")
5418                || modifier.to_uppercase().starts_with("SET ")
5419                || modifier.to_uppercase().starts_with("MULTISET ");
5420            let skip_teradata = is_teradata_modifier
5421                && !matches!(self.config.dialect, Some(DialectType::Teradata));
5422            if !skip_transient && !skip_teradata {
5423                self.write_space();
5424                self.write_keyword(modifier);
5425            }
5426        }
5427
5428        if !is_dictionary {
5429            self.write_space();
5430            self.write_keyword("TABLE");
5431        }
5432
5433        if ct.if_not_exists {
5434            self.write_space();
5435            self.write_keyword("IF NOT EXISTS");
5436        }
5437
5438        self.write_space();
5439        self.generate_table(&ct.name)?;
5440
5441        // ClickHouse: ON CLUSTER clause
5442        if let Some(ref on_cluster) = ct.on_cluster {
5443            self.write_space();
5444            self.generate_on_cluster(on_cluster)?;
5445        }
5446
5447        // Teradata: options after table name before column list (comma-separated)
5448        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Teradata))
5449            && !ct.teradata_post_name_options.is_empty()
5450        {
5451            for opt in &ct.teradata_post_name_options {
5452                self.write(", ");
5453                self.write(opt);
5454            }
5455        }
5456
5457        // Snowflake: COPY GRANTS clause
5458        if ct.copy_grants {
5459            self.write_space();
5460            self.write_keyword("COPY GRANTS");
5461        }
5462
5463        // Snowflake: USING TEMPLATE clause (before columns or AS SELECT)
5464        if let Some(ref using_template) = ct.using_template {
5465            self.write_space();
5466            self.write_keyword("USING TEMPLATE");
5467            self.write_space();
5468            self.generate_expression(using_template)?;
5469            return Ok(());
5470        }
5471
5472        // Handle [SHALLOW | DEEP] CLONE/COPY source_table [AT(...) | BEFORE(...)]
5473        if let Some(ref clone_source) = ct.clone_source {
5474            self.write_space();
5475            if ct.is_copy && self.config.supports_table_copy {
5476                // BigQuery uses COPY
5477                self.write_keyword("COPY");
5478            } else if ct.shallow_clone {
5479                self.write_keyword("SHALLOW CLONE");
5480            } else {
5481                self.write_keyword("CLONE");
5482            }
5483            self.write_space();
5484            self.generate_table(clone_source)?;
5485            // Generate AT/BEFORE time travel clause (stored as Raw expression)
5486            if let Some(ref at_clause) = ct.clone_at_clause {
5487                self.write_space();
5488                self.generate_expression(at_clause)?;
5489            }
5490            return Ok(());
5491        }
5492
5493        // Handle PARTITION OF property
5494        // Output order: PARTITION OF <table> (<columns/constraints>) FOR VALUES ...
5495        // Columns/constraints must appear BETWEEN the table name and the partition bound spec
5496        if let Some(ref partition_of) = ct.partition_of {
5497            self.write_space();
5498
5499            // Extract the PartitionedOfProperty parts to generate them separately
5500            if let Expression::PartitionedOfProperty(ref pop) = partition_of {
5501                // Output: PARTITION OF <table>
5502                self.write_keyword("PARTITION OF");
5503                self.write_space();
5504                self.generate_expression(&pop.this)?;
5505
5506                // Output columns/constraints if present (e.g., (unitsales DEFAULT 0) or (CONSTRAINT ...))
5507                if !ct.columns.is_empty() || !ct.constraints.is_empty() {
5508                    self.write(" (");
5509                    let mut first = true;
5510                    for col in &ct.columns {
5511                        if !first {
5512                            self.write(", ");
5513                        }
5514                        first = false;
5515                        self.generate_column_def(col)?;
5516                    }
5517                    for constraint in &ct.constraints {
5518                        if !first {
5519                            self.write(", ");
5520                        }
5521                        first = false;
5522                        self.generate_table_constraint(constraint)?;
5523                    }
5524                    self.write(")");
5525                }
5526
5527                // Output partition bound spec: FOR VALUES ... or DEFAULT
5528                if let Expression::PartitionBoundSpec(_) = pop.expression.as_ref() {
5529                    self.write_space();
5530                    self.write_keyword("FOR VALUES");
5531                    self.write_space();
5532                    self.generate_expression(&pop.expression)?;
5533                } else {
5534                    self.write_space();
5535                    self.write_keyword("DEFAULT");
5536                }
5537            } else {
5538                // Fallback: generate the whole expression if it's not a PartitionedOfProperty
5539                self.generate_expression(partition_of)?;
5540
5541                // Output columns/constraints if present
5542                if !ct.columns.is_empty() || !ct.constraints.is_empty() {
5543                    self.write(" (");
5544                    let mut first = true;
5545                    for col in &ct.columns {
5546                        if !first {
5547                            self.write(", ");
5548                        }
5549                        first = false;
5550                        self.generate_column_def(col)?;
5551                    }
5552                    for constraint in &ct.constraints {
5553                        if !first {
5554                            self.write(", ");
5555                        }
5556                        first = false;
5557                        self.generate_table_constraint(constraint)?;
5558                    }
5559                    self.write(")");
5560                }
5561            }
5562
5563            // Output table properties (e.g., PARTITION BY RANGE(population))
5564            for prop in &ct.properties {
5565                self.write_space();
5566                self.generate_expression(prop)?;
5567            }
5568
5569            return Ok(());
5570        }
5571
5572        // SQLite: Inline single-column PRIMARY KEY constraints into column definition
5573        // This matches Python sqlglot's behavior for SQLite dialect
5574        self.sqlite_inline_pk_columns.clear();
5575        if matches!(self.config.dialect, Some(crate::dialects::DialectType::SQLite)) {
5576            for constraint in &ct.constraints {
5577                if let TableConstraint::PrimaryKey { columns, name, .. } = constraint {
5578                    // Only inline if: single column, no constraint name, and column exists in table
5579                    if columns.len() == 1 && name.is_none() {
5580                        let pk_col_name = columns[0].name.to_lowercase();
5581                        // Check if this column exists in the table
5582                        if ct.columns.iter().any(|c| c.name.name.to_lowercase() == pk_col_name) {
5583                            self.sqlite_inline_pk_columns.insert(pk_col_name);
5584                        }
5585                    }
5586                }
5587            }
5588        }
5589
5590        // Output columns if present (even for CTAS with columns)
5591        if !ct.columns.is_empty() {
5592            if self.config.pretty {
5593                // Pretty print: each column on new line
5594                self.write(" (");
5595                self.write_newline();
5596                self.indent_level += 1;
5597                for (i, col) in ct.columns.iter().enumerate() {
5598                    if i > 0 {
5599                        self.write(",");
5600                        self.write_newline();
5601                    }
5602                    self.write_indent();
5603                    self.generate_column_def(col)?;
5604                }
5605                // Table constraints (skip inlined PRIMARY KEY for SQLite)
5606                for constraint in &ct.constraints {
5607                    // Skip single-column PRIMARY KEY that was inlined for SQLite
5608                    if let TableConstraint::PrimaryKey { columns, name, .. } = constraint {
5609                        if columns.len() == 1 && name.is_none() &&
5610                           self.sqlite_inline_pk_columns.contains(&columns[0].name.to_lowercase()) {
5611                            continue;
5612                        }
5613                    }
5614                    self.write(",");
5615                    self.write_newline();
5616                    self.write_indent();
5617                    self.generate_table_constraint(constraint)?;
5618                }
5619                self.indent_level -= 1;
5620                self.write_newline();
5621                self.write(")");
5622            } else {
5623                self.write(" (");
5624                for (i, col) in ct.columns.iter().enumerate() {
5625                    if i > 0 {
5626                        self.write(", ");
5627                    }
5628                    self.generate_column_def(col)?;
5629                }
5630                // Table constraints (skip inlined PRIMARY KEY for SQLite)
5631                let mut first_constraint = true;
5632                for constraint in &ct.constraints {
5633                    // Skip single-column PRIMARY KEY that was inlined for SQLite
5634                    if let TableConstraint::PrimaryKey { columns, name, .. } = constraint {
5635                        if columns.len() == 1 && name.is_none() &&
5636                           self.sqlite_inline_pk_columns.contains(&columns[0].name.to_lowercase()) {
5637                            continue;
5638                        }
5639                    }
5640                    if first_constraint {
5641                        self.write(", ");
5642                        first_constraint = false;
5643                    } else {
5644                        self.write(", ");
5645                    }
5646                    self.generate_table_constraint(constraint)?;
5647                }
5648                self.write(")");
5649            }
5650        } else if !ct.constraints.is_empty() {
5651            // No columns but constraints exist (e.g., CREATE TABLE A LIKE B or CREATE TABLE A TAG (...))
5652            let has_like_only = ct.constraints.iter().all(|c| matches!(c, TableConstraint::Like { .. }));
5653            let has_tags_only = ct.constraints.iter().all(|c| matches!(c, TableConstraint::Tags(_)));
5654            // MySQL: CREATE TABLE A LIKE B (no parens)
5655            // Snowflake: CREATE TABLE A TAG (...) (no outer parens, but TAG has its own)
5656            // PostgreSQL and others: CREATE TABLE A (LIKE B INCLUDING ALL) (with parens)
5657            let is_mysql = matches!(self.config.dialect, Some(crate::dialects::DialectType::MySQL) | Some(crate::dialects::DialectType::SingleStore) | Some(crate::dialects::DialectType::TiDB));
5658            let use_parens = !(has_like_only && is_mysql) && !has_tags_only;
5659            if self.config.pretty && use_parens {
5660                self.write(" (");
5661                self.write_newline();
5662                self.indent_level += 1;
5663                for (i, constraint) in ct.constraints.iter().enumerate() {
5664                    if i > 0 {
5665                        self.write(",");
5666                        self.write_newline();
5667                    }
5668                    self.write_indent();
5669                    self.generate_table_constraint(constraint)?;
5670                }
5671                self.indent_level -= 1;
5672                self.write_newline();
5673                self.write(")");
5674            } else {
5675                if use_parens {
5676                    self.write(" (");
5677                } else {
5678                    self.write_space();
5679                }
5680                for (i, constraint) in ct.constraints.iter().enumerate() {
5681                    if i > 0 {
5682                        self.write(", ");
5683                    }
5684                    self.generate_table_constraint(constraint)?;
5685                }
5686                if use_parens {
5687                    self.write(")");
5688                }
5689            }
5690        }
5691
5692        // TSQL ON filegroup or ON filegroup (partition_column) clause
5693        if let Some(ref on_prop) = ct.on_property {
5694            self.write(" ");
5695            self.write_keyword("ON");
5696            self.write(" ");
5697            self.generate_expression(&on_prop.this)?;
5698        }
5699
5700        // Output SchemaCommentProperty BEFORE WITH properties (Presto/Hive/Spark style)
5701        // For ClickHouse, SchemaCommentProperty goes after AS SELECT, handled later
5702        if !is_clickhouse {
5703            for prop in &ct.properties {
5704                if let Expression::SchemaCommentProperty(_) = prop {
5705                    if self.config.pretty {
5706                        self.write_newline();
5707                    } else {
5708                        self.write_space();
5709                    }
5710                    self.generate_expression(prop)?;
5711                }
5712            }
5713        }
5714
5715        // WITH properties (output after columns if columns exist, otherwise before AS)
5716        if !ct.with_properties.is_empty() {
5717            // Snowflake ICEBERG/DYNAMIC TABLE: output properties inline (space-separated, no WITH wrapper)
5718            let is_snowflake_special_table = matches!(self.config.dialect, Some(crate::dialects::DialectType::Snowflake))
5719                && (ct.table_modifier.as_deref() == Some("ICEBERG") || ct.table_modifier.as_deref() == Some("DYNAMIC"));
5720            if is_snowflake_special_table {
5721                for (key, value) in &ct.with_properties {
5722                    self.write_space();
5723                    self.write(key);
5724                    self.write("=");
5725                    self.write(value);
5726                }
5727            } else if self.config.pretty {
5728                self.write_newline();
5729                self.write_keyword("WITH");
5730                self.write(" (");
5731                self.write_newline();
5732                self.indent_level += 1;
5733                for (i, (key, value)) in ct.with_properties.iter().enumerate() {
5734                    if i > 0 {
5735                        self.write(",");
5736                        self.write_newline();
5737                    }
5738                    self.write_indent();
5739                    self.write(key);
5740                    self.write("=");
5741                    self.write(value);
5742                }
5743                self.indent_level -= 1;
5744                self.write_newline();
5745                self.write(")");
5746            } else {
5747                self.write_space();
5748                self.write_keyword("WITH");
5749                self.write(" (");
5750                for (i, (key, value)) in ct.with_properties.iter().enumerate() {
5751                    if i > 0 {
5752                        self.write(", ");
5753                    }
5754                    self.write(key);
5755                    self.write("=");
5756                    self.write(value);
5757                }
5758                self.write(")");
5759            }
5760        }
5761
5762        let (pre_as_properties, post_as_properties): (Vec<&Expression>, Vec<&Expression>) = if is_clickhouse && ct.as_select.is_some() {
5763            let mut pre = Vec::new();
5764            let mut post = Vec::new();
5765            for prop in &ct.properties {
5766                if matches!(prop, Expression::SchemaCommentProperty(_)) {
5767                    post.push(prop);
5768                } else {
5769                    pre.push(prop);
5770                }
5771            }
5772            (pre, post)
5773        } else {
5774            (ct.properties.iter().collect(), Vec::new())
5775        };
5776
5777        // Table properties like DEFAULT COLLATE (BigQuery), OPTIONS (...), TBLPROPERTIES (...), or PROPERTIES (...)
5778        for prop in pre_as_properties {
5779            // SchemaCommentProperty was already output before WITH properties (except for ClickHouse)
5780            if !is_clickhouse && matches!(prop, Expression::SchemaCommentProperty(_)) {
5781                continue;
5782            }
5783            if self.config.pretty {
5784                self.write_newline();
5785            } else {
5786                self.write_space();
5787            }
5788            // BigQuery: Properties containing OPTIONS should be wrapped with OPTIONS (...)
5789            // Hive: Properties should be wrapped with TBLPROPERTIES (...)
5790            // Doris/StarRocks: Properties should be wrapped with PROPERTIES (...)
5791            if let Expression::Properties(props) = prop {
5792                let is_hive_dialect = matches!(self.config.dialect, Some(crate::dialects::DialectType::Hive) | Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Athena));
5793                let is_doris_starrocks = matches!(self.config.dialect, Some(crate::dialects::DialectType::Doris) | Some(crate::dialects::DialectType::StarRocks));
5794                if is_hive_dialect {
5795                    self.generate_tblproperties_clause(&props.expressions)?;
5796                } else if is_doris_starrocks {
5797                    self.generate_properties_clause(&props.expressions)?;
5798                } else {
5799                    self.generate_options_clause(&props.expressions)?;
5800                }
5801            } else {
5802                self.generate_expression(prop)?;
5803            }
5804        }
5805
5806        // Post-table properties like TSQL WITH(SYSTEM_VERSIONING=ON(...)) or Doris PROPERTIES
5807        for prop in &ct.post_table_properties {
5808            if let Expression::WithSystemVersioningProperty(ref svp) = prop {
5809                self.write(" WITH(");
5810                self.generate_system_versioning_content(svp)?;
5811                self.write(")");
5812            } else if let Expression::Properties(props) = prop {
5813                // Doris/StarRocks: PROPERTIES ('key'='value', ...) in post_table_properties
5814                let is_doris_starrocks = matches!(self.config.dialect, Some(crate::dialects::DialectType::Doris) | Some(crate::dialects::DialectType::StarRocks));
5815                self.write_space();
5816                if is_doris_starrocks {
5817                    self.generate_properties_clause(&props.expressions)?;
5818                } else {
5819                    self.generate_options_clause(&props.expressions)?;
5820                }
5821            } else {
5822                self.write_space();
5823                self.generate_expression(prop)?;
5824            }
5825        }
5826
5827        // StarRocks ROLLUP property: ROLLUP (r1(col1, col2), r2(col1))
5828        // Only output for StarRocks target
5829        if let Some(ref rollup) = ct.rollup {
5830            if matches!(self.config.dialect, Some(DialectType::StarRocks)) {
5831                self.write_space();
5832                self.generate_rollup_property(rollup)?;
5833            }
5834        }
5835
5836        // MySQL table options (ENGINE=val, AUTO_INCREMENT=val, etc.)
5837        // Only output for MySQL-compatible dialects; strip for others during transpilation
5838        // COMMENT is also used by Hive/Spark so we selectively preserve it
5839        let is_mysql_compatible = matches!(self.config.dialect,
5840            Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::Doris) | Some(DialectType::StarRocks) | None
5841        );
5842        let is_hive_compatible = matches!(self.config.dialect,
5843            Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Athena)
5844        );
5845        let mysql_pretty_options = self.config.pretty && matches!(self.config.dialect, Some(DialectType::MySQL));
5846        for (key, value) in &ct.mysql_table_options {
5847            // Skip non-MySQL-specific options for non-MySQL targets
5848            let should_output = if is_mysql_compatible {
5849                true
5850            } else if is_hive_compatible && key == "COMMENT" {
5851                true // COMMENT is valid in Hive/Spark table definitions
5852            } else {
5853                false
5854            };
5855            if should_output {
5856                if mysql_pretty_options {
5857                    self.write_newline();
5858                    self.write_indent();
5859                } else {
5860                    self.write_space();
5861                }
5862                self.write_keyword(key);
5863                // StarRocks/Doris: COMMENT 'value' (no =), others: COMMENT='value'
5864                if key == "COMMENT" && !self.config.schema_comment_with_eq {
5865                    self.write_space();
5866                } else {
5867                    self.write("=");
5868                }
5869                self.write(value);
5870            }
5871        }
5872
5873        // Spark/Databricks: USING PARQUET for temporary tables that don't already have a storage format
5874        if ct.temporary
5875            && matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks))
5876            && ct.as_select.is_none()
5877        {
5878            self.write_space();
5879            self.write_keyword("USING PARQUET");
5880        }
5881
5882        // PostgreSQL INHERITS clause
5883        if !ct.inherits.is_empty() {
5884            self.write_space();
5885            self.write_keyword("INHERITS");
5886            self.write(" (");
5887            for (i, parent) in ct.inherits.iter().enumerate() {
5888                if i > 0 {
5889                    self.write(", ");
5890                }
5891                self.generate_table(parent)?;
5892            }
5893            self.write(")");
5894        }
5895
5896        // CREATE TABLE AS SELECT
5897        if let Some(ref query) = ct.as_select {
5898            self.write_space();
5899            self.write_keyword("AS");
5900            self.write_space();
5901            if ct.as_select_parenthesized {
5902                self.write("(");
5903            }
5904            self.generate_expression(query)?;
5905            if ct.as_select_parenthesized {
5906                self.write(")");
5907            }
5908
5909            // Teradata: WITH DATA / WITH NO DATA
5910            if let Some(with_data) = ct.with_data {
5911                self.write_space();
5912                self.write_keyword("WITH");
5913                if !with_data {
5914                    self.write_space();
5915                    self.write_keyword("NO");
5916                }
5917                self.write_space();
5918                self.write_keyword("DATA");
5919            }
5920
5921            // Teradata: AND STATISTICS / AND NO STATISTICS
5922            if let Some(with_statistics) = ct.with_statistics {
5923                self.write_space();
5924                self.write_keyword("AND");
5925                if !with_statistics {
5926                    self.write_space();
5927                    self.write_keyword("NO");
5928                }
5929                self.write_space();
5930                self.write_keyword("STATISTICS");
5931            }
5932
5933            // Teradata: Index specifications
5934            for index in &ct.teradata_indexes {
5935                self.write_space();
5936                match index.kind {
5937                    TeradataIndexKind::NoPrimary => {
5938                        self.write_keyword("NO PRIMARY INDEX");
5939                    }
5940                    TeradataIndexKind::Primary => {
5941                        self.write_keyword("PRIMARY INDEX");
5942                    }
5943                    TeradataIndexKind::PrimaryAmp => {
5944                        self.write_keyword("PRIMARY AMP INDEX");
5945                    }
5946                    TeradataIndexKind::Unique => {
5947                        self.write_keyword("UNIQUE INDEX");
5948                    }
5949                    TeradataIndexKind::UniquePrimary => {
5950                        self.write_keyword("UNIQUE PRIMARY INDEX");
5951                    }
5952                    TeradataIndexKind::Secondary => {
5953                        self.write_keyword("INDEX");
5954                    }
5955                }
5956                // Output index name if present
5957                if let Some(ref name) = index.name {
5958                    self.write_space();
5959                    self.write(name);
5960                }
5961                // Output columns if present
5962                if !index.columns.is_empty() {
5963                    self.write(" (");
5964                    for (i, col) in index.columns.iter().enumerate() {
5965                        if i > 0 {
5966                            self.write(", ");
5967                        }
5968                        self.write(col);
5969                    }
5970                    self.write(")");
5971                }
5972            }
5973
5974            // Teradata: ON COMMIT behavior for volatile tables
5975            if let Some(ref on_commit) = ct.on_commit {
5976                self.write_space();
5977                self.write_keyword("ON COMMIT");
5978                self.write_space();
5979                match on_commit {
5980                    OnCommit::PreserveRows => self.write_keyword("PRESERVE ROWS"),
5981                    OnCommit::DeleteRows => self.write_keyword("DELETE ROWS"),
5982                }
5983            }
5984
5985            if !post_as_properties.is_empty() {
5986                for prop in post_as_properties {
5987                    self.write_space();
5988                    self.generate_expression(prop)?;
5989                }
5990            }
5991
5992            // Restore Athena Hive context before early return
5993            self.athena_hive_context = saved_athena_hive_context;
5994            return Ok(());
5995        }
5996
5997        // ON COMMIT behavior (for non-CTAS tables)
5998        if let Some(ref on_commit) = ct.on_commit {
5999            self.write_space();
6000            self.write_keyword("ON COMMIT");
6001            self.write_space();
6002            match on_commit {
6003                OnCommit::PreserveRows => self.write_keyword("PRESERVE ROWS"),
6004                OnCommit::DeleteRows => self.write_keyword("DELETE ROWS"),
6005            }
6006        }
6007
6008        // Restore Athena Hive context
6009        self.athena_hive_context = saved_athena_hive_context;
6010
6011        Ok(())
6012    }
6013
6014    /// Generate column definition as an expression (for ROWS FROM alias columns, XMLTABLE/JSON_TABLE)
6015    /// Outputs: "col_name" TYPE [PATH 'xpath'] (not the full CREATE TABLE column definition)
6016    fn generate_column_def_expr(&mut self, col: &ColumnDef) -> Result<()> {
6017        // Output column name
6018        self.generate_identifier(&col.name)?;
6019        // Output data type if known
6020        if !matches!(col.data_type, DataType::Unknown) {
6021            self.write_space();
6022            self.generate_data_type(&col.data_type)?;
6023        }
6024        // Output PATH constraint if present (for XMLTABLE/JSON_TABLE columns)
6025        for constraint in &col.constraints {
6026            if let ColumnConstraint::Path(path_expr) = constraint {
6027                self.write_space();
6028                self.write_keyword("PATH");
6029                self.write_space();
6030                self.generate_expression(path_expr)?;
6031            }
6032        }
6033        Ok(())
6034    }
6035
6036    fn generate_column_def(&mut self, col: &ColumnDef) -> Result<()> {
6037        // Check if this is a TSQL computed column (no data type)
6038        let has_computed_no_type = matches!(&col.data_type, DataType::Custom { name } if name.is_empty())
6039            && col.constraints.iter().any(|c| matches!(c, ColumnConstraint::ComputedColumn(_)));
6040        // Some dialects (notably TSQL/Fabric) do not include an explicit type for computed columns.
6041        let omit_computed_type = !self.config.computed_column_with_type
6042            && col.constraints.iter().any(|c| matches!(c, ColumnConstraint::ComputedColumn(_)));
6043
6044        // Check if this is a partition column spec (no data type, type is Unknown)
6045        // This is used in PostgreSQL PARTITION OF syntax where columns only have constraints
6046        let is_partition_column_spec = matches!(col.data_type, DataType::Unknown);
6047
6048        // Check if this is a DYNAMIC TABLE column (no data type, empty Custom name, no constraints)
6049        // Also check the no_type flag for SQLite columns without types
6050        let has_no_type = col.no_type || (matches!(&col.data_type, DataType::Custom { name } if name.is_empty())
6051            && col.constraints.is_empty());
6052
6053        self.generate_identifier(&col.name)?;
6054
6055        // Check for SERIAL/BIGSERIAL/SMALLSERIAL expansion for Materialize and PostgreSQL
6056        let serial_expansion = if matches!(self.config.dialect, Some(DialectType::Materialize) | Some(DialectType::PostgreSQL)) {
6057            if let DataType::Custom { ref name } = col.data_type {
6058                match name.to_uppercase().as_str() {
6059                    "SERIAL" => Some("INT"),
6060                    "BIGSERIAL" => Some("BIGINT"),
6061                    "SMALLSERIAL" => Some("SMALLINT"),
6062                    _ => None,
6063                }
6064            } else {
6065                None
6066            }
6067        } else {
6068            None
6069        };
6070
6071        if !has_computed_no_type && !omit_computed_type && !is_partition_column_spec && !has_no_type {
6072            self.write_space();
6073            if let Some(int_type) = serial_expansion {
6074                // SERIAL -> INT (+ constraints added below)
6075                self.write_keyword(int_type);
6076            } else if col.unsigned && matches!(self.config.dialect, Some(DialectType::DuckDB)) {
6077                // For DuckDB: convert unsigned integer types to their unsigned equivalents
6078                let unsigned_type = match &col.data_type {
6079                    DataType::Int { .. } => Some("UINTEGER"),
6080                    DataType::BigInt { .. } => Some("UBIGINT"),
6081                    DataType::SmallInt { .. } => Some("USMALLINT"),
6082                    DataType::TinyInt { .. } => Some("UTINYINT"),
6083                    _ => None,
6084                };
6085                if let Some(utype) = unsigned_type {
6086                    self.write_keyword(utype);
6087                } else {
6088                    self.generate_data_type(&col.data_type)?;
6089                }
6090            } else {
6091                self.generate_data_type(&col.data_type)?;
6092            }
6093        }
6094
6095        // MySQL type modifiers (must come right after data type)
6096        // Skip UNSIGNED for DuckDB (already mapped to unsigned type above)
6097        if col.unsigned && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
6098            self.write_space();
6099            self.write_keyword("UNSIGNED");
6100        }
6101        if col.zerofill {
6102            self.write_space();
6103            self.write_keyword("ZEROFILL");
6104        }
6105
6106        // Teradata column attributes (must come right after data type, in specific order)
6107        // ORDER: CHARACTER SET, UPPERCASE, CASESPECIFIC, FORMAT, TITLE, INLINE LENGTH, COMPRESS
6108
6109        if let Some(ref charset) = col.character_set {
6110            self.write_space();
6111            self.write_keyword("CHARACTER SET");
6112            self.write_space();
6113            self.write(charset);
6114        }
6115
6116        if col.uppercase {
6117            self.write_space();
6118            self.write_keyword("UPPERCASE");
6119        }
6120
6121        if let Some(casespecific) = col.casespecific {
6122            self.write_space();
6123            if casespecific {
6124                self.write_keyword("CASESPECIFIC");
6125            } else {
6126                self.write_keyword("NOT CASESPECIFIC");
6127            }
6128        }
6129
6130        if let Some(ref format) = col.format {
6131            self.write_space();
6132            self.write_keyword("FORMAT");
6133            self.write(" '");
6134            self.write(format);
6135            self.write("'");
6136        }
6137
6138        if let Some(ref title) = col.title {
6139            self.write_space();
6140            self.write_keyword("TITLE");
6141            self.write(" '");
6142            self.write(title);
6143            self.write("'");
6144        }
6145
6146        if let Some(length) = col.inline_length {
6147            self.write_space();
6148            self.write_keyword("INLINE LENGTH");
6149            self.write(" ");
6150            self.write(&length.to_string());
6151        }
6152
6153        if let Some(ref compress) = col.compress {
6154            self.write_space();
6155            self.write_keyword("COMPRESS");
6156            if !compress.is_empty() {
6157                // Single string literal: output without parentheses (Teradata syntax)
6158                if compress.len() == 1 {
6159                    if let Expression::Literal(Literal::String(_)) = &compress[0] {
6160                        self.write_space();
6161                        self.generate_expression(&compress[0])?;
6162                    } else {
6163                        self.write(" (");
6164                        self.generate_expression(&compress[0])?;
6165                        self.write(")");
6166                    }
6167                } else {
6168                    self.write(" (");
6169                    for (i, val) in compress.iter().enumerate() {
6170                        if i > 0 {
6171                            self.write(", ");
6172                        }
6173                        self.generate_expression(val)?;
6174                    }
6175                    self.write(")");
6176                }
6177            }
6178        }
6179
6180        // Column constraints - output in original order if constraint_order is populated
6181        // Otherwise fall back to legacy fixed order for backward compatibility
6182        if !col.constraint_order.is_empty() {
6183            // Use constraint_order for original ordering
6184            // Track indices for constraints stored in the constraints Vec
6185            let mut references_idx = 0;
6186            let mut check_idx = 0;
6187            let mut generated_idx = 0;
6188            let mut collate_idx = 0;
6189            let mut comment_idx = 0;
6190            // The preprocessing in dialects/mod.rs now handles the correct ordering of
6191            // NOT NULL relative to IDENTITY for PostgreSQL, so no deferral needed here.
6192            let defer_not_null_after_identity = false;
6193            let mut pending_not_null_after_identity = false;
6194
6195            for constraint_type in &col.constraint_order {
6196                match constraint_type {
6197                    ConstraintType::PrimaryKey => {
6198                        // Materialize doesn't support PRIMARY KEY column constraints
6199                        if col.primary_key && !matches!(self.config.dialect, Some(DialectType::Materialize)) {
6200                            if let Some(ref cname) = col.primary_key_constraint_name {
6201                                self.write_space();
6202                                self.write_keyword("CONSTRAINT");
6203                                self.write_space();
6204                                self.write(cname);
6205                            }
6206                            self.write_space();
6207                            self.write_keyword("PRIMARY KEY");
6208                            if let Some(ref order) = col.primary_key_order {
6209                                self.write_space();
6210                                match order {
6211                                    SortOrder::Asc => self.write_keyword("ASC"),
6212                                    SortOrder::Desc => self.write_keyword("DESC"),
6213                                }
6214                            }
6215                        }
6216                    }
6217                    ConstraintType::Unique => {
6218                        if col.unique {
6219                            if let Some(ref cname) = col.unique_constraint_name {
6220                                self.write_space();
6221                                self.write_keyword("CONSTRAINT");
6222                                self.write_space();
6223                                self.write(cname);
6224                            }
6225                            self.write_space();
6226                            self.write_keyword("UNIQUE");
6227                            // PostgreSQL 15+: NULLS NOT DISTINCT
6228                            if col.unique_nulls_not_distinct {
6229                                self.write(" NULLS NOT DISTINCT");
6230                            }
6231                        }
6232                    }
6233                    ConstraintType::NotNull => {
6234                        if col.nullable == Some(false) {
6235                            if defer_not_null_after_identity {
6236                                pending_not_null_after_identity = true;
6237                                continue;
6238                            }
6239                            if let Some(ref cname) = col.not_null_constraint_name {
6240                                self.write_space();
6241                                self.write_keyword("CONSTRAINT");
6242                                self.write_space();
6243                                self.write(cname);
6244                            }
6245                            self.write_space();
6246                            self.write_keyword("NOT NULL");
6247                        }
6248                    }
6249                    ConstraintType::Null => {
6250                        if col.nullable == Some(true) {
6251                            self.write_space();
6252                            self.write_keyword("NULL");
6253                        }
6254                    }
6255                    ConstraintType::Default => {
6256                        if let Some(ref default) = col.default {
6257                            self.write_space();
6258                            self.write_keyword("DEFAULT");
6259                            self.write_space();
6260                            self.generate_expression(default)?;
6261                        }
6262                    }
6263                    ConstraintType::AutoIncrement => {
6264                        if col.auto_increment {
6265                            // DuckDB doesn't support AUTO_INCREMENT - skip entirely
6266                            if matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB)) {
6267                                // Skip - DuckDB uses sequences or rowid instead
6268                            } else if matches!(self.config.dialect, Some(crate::dialects::DialectType::Materialize)) {
6269                                // Materialize strips AUTO_INCREMENT but adds NOT NULL
6270                                if !matches!(col.nullable, Some(false)) {
6271                                    self.write_space();
6272                                    self.write_keyword("NOT NULL");
6273                                }
6274                            } else {
6275                            self.write_space();
6276                            self.generate_auto_increment_keyword(col)?;
6277                            if pending_not_null_after_identity {
6278                                self.write_space();
6279                                self.write_keyword("NOT NULL");
6280                                pending_not_null_after_identity = false;
6281                            }
6282                        }
6283                        } // close else for DuckDB skip
6284                    }
6285                    ConstraintType::References => {
6286                        // Find next References constraint
6287                        while references_idx < col.constraints.len() {
6288                            if let ColumnConstraint::References(fk_ref) = &col.constraints[references_idx] {
6289                                // CONSTRAINT name if present
6290                                if let Some(ref name) = fk_ref.constraint_name {
6291                                    self.write_space();
6292                                    self.write_keyword("CONSTRAINT");
6293                                    self.write_space();
6294                                    self.write(name);
6295                                }
6296                                self.write_space();
6297                                if fk_ref.has_foreign_key_keywords {
6298                                    self.write_keyword("FOREIGN KEY");
6299                                    self.write_space();
6300                                }
6301                                self.write_keyword("REFERENCES");
6302                                self.write_space();
6303                                self.generate_table(&fk_ref.table)?;
6304                                if !fk_ref.columns.is_empty() {
6305                                    self.write(" (");
6306                                    for (i, c) in fk_ref.columns.iter().enumerate() {
6307                                        if i > 0 {
6308                                            self.write(", ");
6309                                        }
6310                                        self.generate_identifier(c)?;
6311                                    }
6312                                    self.write(")");
6313                                }
6314                                self.generate_referential_actions(fk_ref)?;
6315                                references_idx += 1;
6316                                break;
6317                            }
6318                            references_idx += 1;
6319                        }
6320                    }
6321                    ConstraintType::Check => {
6322                        // Find next Check constraint
6323                        while check_idx < col.constraints.len() {
6324                            if let ColumnConstraint::Check(expr) = &col.constraints[check_idx] {
6325                                // Output CONSTRAINT name if present (only for first CHECK)
6326                                if check_idx == 0 {
6327                                    if let Some(ref cname) = col.check_constraint_name {
6328                                        self.write_space();
6329                                        self.write_keyword("CONSTRAINT");
6330                                        self.write_space();
6331                                        self.write(cname);
6332                                    }
6333                                }
6334                                self.write_space();
6335                                self.write_keyword("CHECK");
6336                                self.write(" (");
6337                                self.generate_expression(expr)?;
6338                                self.write(")");
6339                                check_idx += 1;
6340                                break;
6341                            }
6342                            check_idx += 1;
6343                        }
6344                    }
6345                    ConstraintType::GeneratedAsIdentity => {
6346                        // Find next GeneratedAsIdentity constraint
6347                        while generated_idx < col.constraints.len() {
6348                            if let ColumnConstraint::GeneratedAsIdentity(gen) = &col.constraints[generated_idx] {
6349                                self.write_space();
6350                                // Redshift uses IDENTITY(start, increment) syntax
6351                                if matches!(self.config.dialect, Some(crate::dialects::DialectType::Redshift)) {
6352                                    self.write_keyword("IDENTITY");
6353                                    self.write("(");
6354                                    if let Some(ref start) = gen.start {
6355                                        self.generate_expression(start)?;
6356                                    } else {
6357                                        self.write("0");
6358                                    }
6359                                    self.write(", ");
6360                                    if let Some(ref incr) = gen.increment {
6361                                        self.generate_expression(incr)?;
6362                                    } else {
6363                                        self.write("1");
6364                                    }
6365                                    self.write(")");
6366                                } else {
6367                                    self.write_keyword("GENERATED");
6368                                    if gen.always {
6369                                        self.write_space();
6370                                        self.write_keyword("ALWAYS");
6371                                    } else {
6372                                        self.write_space();
6373                                        self.write_keyword("BY DEFAULT");
6374                                        if gen.on_null {
6375                                            self.write_space();
6376                                            self.write_keyword("ON NULL");
6377                                        }
6378                                    }
6379                                    self.write_space();
6380                                    self.write_keyword("AS IDENTITY");
6381
6382                                    let has_options = gen.start.is_some() || gen.increment.is_some()
6383                                        || gen.minvalue.is_some() || gen.maxvalue.is_some() || gen.cycle.is_some();
6384                                    if has_options {
6385                                        self.write(" (");
6386                                        let mut first = true;
6387                                        if let Some(ref start) = gen.start {
6388                                            if !first { self.write(" "); }
6389                                            first = false;
6390                                            self.write_keyword("START WITH");
6391                                            self.write_space();
6392                                            self.generate_expression(start)?;
6393                                        }
6394                                        if let Some(ref incr) = gen.increment {
6395                                            if !first { self.write(" "); }
6396                                            first = false;
6397                                            self.write_keyword("INCREMENT BY");
6398                                            self.write_space();
6399                                            self.generate_expression(incr)?;
6400                                        }
6401                                        if let Some(ref minv) = gen.minvalue {
6402                                            if !first { self.write(" "); }
6403                                            first = false;
6404                                            self.write_keyword("MINVALUE");
6405                                            self.write_space();
6406                                            self.generate_expression(minv)?;
6407                                        }
6408                                        if let Some(ref maxv) = gen.maxvalue {
6409                                            if !first { self.write(" "); }
6410                                            first = false;
6411                                            self.write_keyword("MAXVALUE");
6412                                            self.write_space();
6413                                            self.generate_expression(maxv)?;
6414                                        }
6415                                        if let Some(cycle) = gen.cycle {
6416                                            if !first { self.write(" "); }
6417                                            if cycle {
6418                                                self.write_keyword("CYCLE");
6419                                            } else {
6420                                                self.write_keyword("NO CYCLE");
6421                                            }
6422                                        }
6423                                        self.write(")");
6424                                    }
6425                                }
6426                                generated_idx += 1;
6427                                break;
6428                            }
6429                            generated_idx += 1;
6430                        }
6431                    }
6432                    ConstraintType::Collate => {
6433                        // Find next Collate constraint
6434                        while collate_idx < col.constraints.len() {
6435                            if let ColumnConstraint::Collate(collation) = &col.constraints[collate_idx] {
6436                                self.write_space();
6437                                self.write_keyword("COLLATE");
6438                                self.write_space();
6439                                self.generate_identifier(collation)?;
6440                                collate_idx += 1;
6441                                break;
6442                            }
6443                            collate_idx += 1;
6444                        }
6445                    }
6446                    ConstraintType::Comment => {
6447                        // Find next Comment constraint
6448                        while comment_idx < col.constraints.len() {
6449                            if let ColumnConstraint::Comment(comment) = &col.constraints[comment_idx] {
6450                                self.write_space();
6451                                self.write_keyword("COMMENT");
6452                                self.write_space();
6453                                self.generate_string_literal(comment)?;
6454                                comment_idx += 1;
6455                                break;
6456                            }
6457                            comment_idx += 1;
6458                        }
6459                    }
6460                    ConstraintType::Tags => {
6461                        // Find next Tags constraint (Snowflake)
6462                        for constraint in &col.constraints {
6463                            if let ColumnConstraint::Tags(tags) = constraint {
6464                                self.write_space();
6465                                self.write_keyword("TAG");
6466                                self.write(" (");
6467                                for (i, expr) in tags.expressions.iter().enumerate() {
6468                                    if i > 0 {
6469                                        self.write(", ");
6470                                    }
6471                                    self.generate_expression(expr)?;
6472                                }
6473                                self.write(")");
6474                                break;
6475                            }
6476                        }
6477                    }
6478                    ConstraintType::ComputedColumn => {
6479                        // Find next ComputedColumn constraint
6480                        for constraint in &col.constraints {
6481                            if let ColumnConstraint::ComputedColumn(cc) = constraint {
6482                                self.write_space();
6483                                self.generate_computed_column_inline(cc)?;
6484                                break;
6485                            }
6486                        }
6487                    }
6488                    ConstraintType::GeneratedAsRow => {
6489                        // Find next GeneratedAsRow constraint
6490                        for constraint in &col.constraints {
6491                            if let ColumnConstraint::GeneratedAsRow(gar) = constraint {
6492                                self.write_space();
6493                                self.generate_generated_as_row_inline(gar)?;
6494                                break;
6495                            }
6496                        }
6497                    }
6498                    ConstraintType::OnUpdate => {
6499                        if let Some(ref expr) = col.on_update {
6500                            self.write_space();
6501                            self.write_keyword("ON UPDATE");
6502                            self.write_space();
6503                            self.generate_expression(expr)?;
6504                        }
6505                    }
6506                    ConstraintType::Encode => {
6507                        if let Some(ref encoding) = col.encoding {
6508                            self.write_space();
6509                            self.write_keyword("ENCODE");
6510                            self.write_space();
6511                            self.write(encoding);
6512                        }
6513                    }
6514                    ConstraintType::Path => {
6515                        // Find next Path constraint
6516                        for constraint in &col.constraints {
6517                            if let ColumnConstraint::Path(path_expr) = constraint {
6518                                self.write_space();
6519                                self.write_keyword("PATH");
6520                                self.write_space();
6521                                self.generate_expression(path_expr)?;
6522                                break;
6523                            }
6524                        }
6525                    }
6526                }
6527            }
6528            if pending_not_null_after_identity {
6529                self.write_space();
6530                self.write_keyword("NOT NULL");
6531            }
6532        } else {
6533            // Legacy fixed order for backward compatibility
6534            if col.primary_key {
6535                self.write_space();
6536                self.write_keyword("PRIMARY KEY");
6537                if let Some(ref order) = col.primary_key_order {
6538                    self.write_space();
6539                    match order {
6540                        SortOrder::Asc => self.write_keyword("ASC"),
6541                        SortOrder::Desc => self.write_keyword("DESC"),
6542                    }
6543                }
6544            }
6545
6546            if col.unique {
6547                self.write_space();
6548                self.write_keyword("UNIQUE");
6549                // PostgreSQL 15+: NULLS NOT DISTINCT
6550                if col.unique_nulls_not_distinct {
6551                    self.write(" NULLS NOT DISTINCT");
6552                }
6553            }
6554
6555            match col.nullable {
6556                Some(false) => {
6557                    self.write_space();
6558                    self.write_keyword("NOT NULL");
6559                }
6560                Some(true) => {
6561                    self.write_space();
6562                    self.write_keyword("NULL");
6563                }
6564                None => {}
6565            }
6566
6567            if let Some(ref default) = col.default {
6568                self.write_space();
6569                self.write_keyword("DEFAULT");
6570                self.write_space();
6571                self.generate_expression(default)?;
6572            }
6573
6574            if col.auto_increment {
6575                self.write_space();
6576                self.generate_auto_increment_keyword(col)?;
6577            }
6578
6579            // Column-level constraints from Vec
6580            for constraint in &col.constraints {
6581                match constraint {
6582                    ColumnConstraint::References(fk_ref) => {
6583                        self.write_space();
6584                        if fk_ref.has_foreign_key_keywords {
6585                            self.write_keyword("FOREIGN KEY");
6586                            self.write_space();
6587                        }
6588                        self.write_keyword("REFERENCES");
6589                        self.write_space();
6590                        self.generate_table(&fk_ref.table)?;
6591                        if !fk_ref.columns.is_empty() {
6592                            self.write(" (");
6593                            for (i, c) in fk_ref.columns.iter().enumerate() {
6594                                if i > 0 {
6595                                    self.write(", ");
6596                                }
6597                                self.generate_identifier(c)?;
6598                            }
6599                            self.write(")");
6600                        }
6601                        self.generate_referential_actions(fk_ref)?;
6602                    }
6603                    ColumnConstraint::Check(expr) => {
6604                        self.write_space();
6605                        self.write_keyword("CHECK");
6606                        self.write(" (");
6607                        self.generate_expression(expr)?;
6608                        self.write(")");
6609                    }
6610                    ColumnConstraint::GeneratedAsIdentity(gen) => {
6611                        self.write_space();
6612                        // Redshift uses IDENTITY(start, increment) syntax
6613                        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Redshift)) {
6614                            self.write_keyword("IDENTITY");
6615                            self.write("(");
6616                            if let Some(ref start) = gen.start {
6617                                self.generate_expression(start)?;
6618                            } else {
6619                                self.write("0");
6620                            }
6621                            self.write(", ");
6622                            if let Some(ref incr) = gen.increment {
6623                                self.generate_expression(incr)?;
6624                            } else {
6625                                self.write("1");
6626                            }
6627                            self.write(")");
6628                        } else {
6629                            self.write_keyword("GENERATED");
6630                            if gen.always {
6631                                self.write_space();
6632                                self.write_keyword("ALWAYS");
6633                            } else {
6634                                self.write_space();
6635                                self.write_keyword("BY DEFAULT");
6636                                if gen.on_null {
6637                                    self.write_space();
6638                                    self.write_keyword("ON NULL");
6639                                }
6640                            }
6641                            self.write_space();
6642                            self.write_keyword("AS IDENTITY");
6643
6644                            let has_options = gen.start.is_some() || gen.increment.is_some()
6645                                || gen.minvalue.is_some() || gen.maxvalue.is_some() || gen.cycle.is_some();
6646                            if has_options {
6647                                self.write(" (");
6648                                let mut first = true;
6649                                if let Some(ref start) = gen.start {
6650                                    if !first { self.write(" "); }
6651                                    first = false;
6652                                    self.write_keyword("START WITH");
6653                                    self.write_space();
6654                                    self.generate_expression(start)?;
6655                                }
6656                                if let Some(ref incr) = gen.increment {
6657                                    if !first { self.write(" "); }
6658                                    first = false;
6659                                    self.write_keyword("INCREMENT BY");
6660                                    self.write_space();
6661                                    self.generate_expression(incr)?;
6662                                }
6663                                if let Some(ref minv) = gen.minvalue {
6664                                    if !first { self.write(" "); }
6665                                    first = false;
6666                                    self.write_keyword("MINVALUE");
6667                                    self.write_space();
6668                                    self.generate_expression(minv)?;
6669                                }
6670                                if let Some(ref maxv) = gen.maxvalue {
6671                                    if !first { self.write(" "); }
6672                                    first = false;
6673                                    self.write_keyword("MAXVALUE");
6674                                    self.write_space();
6675                                    self.generate_expression(maxv)?;
6676                                }
6677                                if let Some(cycle) = gen.cycle {
6678                                    if !first { self.write(" "); }
6679                                    if cycle {
6680                                        self.write_keyword("CYCLE");
6681                                    } else {
6682                                        self.write_keyword("NO CYCLE");
6683                                    }
6684                                }
6685                                self.write(")");
6686                            }
6687                        }
6688                    }
6689                    ColumnConstraint::Collate(collation) => {
6690                        self.write_space();
6691                        self.write_keyword("COLLATE");
6692                        self.write_space();
6693                        self.generate_identifier(collation)?;
6694                    }
6695                    ColumnConstraint::Comment(comment) => {
6696                        self.write_space();
6697                        self.write_keyword("COMMENT");
6698                        self.write_space();
6699                        self.generate_string_literal(comment)?;
6700                    }
6701                    ColumnConstraint::Path(path_expr) => {
6702                        self.write_space();
6703                        self.write_keyword("PATH");
6704                        self.write_space();
6705                        self.generate_expression(path_expr)?;
6706                    }
6707                    _ => {} // Other constraints handled above
6708                }
6709            }
6710
6711            // Redshift: ENCODE encoding_type (legacy path)
6712            if let Some(ref encoding) = col.encoding {
6713                self.write_space();
6714                self.write_keyword("ENCODE");
6715                self.write_space();
6716                self.write(encoding);
6717            }
6718        }
6719
6720        // ClickHouse: CODEC(...)
6721        if let Some(ref codec) = col.codec {
6722            self.write_space();
6723            self.write_keyword("CODEC");
6724            self.write("(");
6725            self.write(codec);
6726            self.write(")");
6727        }
6728
6729        // ClickHouse: EPHEMERAL [expr]
6730        if let Some(ref ephemeral) = col.ephemeral {
6731            self.write_space();
6732            self.write_keyword("EPHEMERAL");
6733            if let Some(ref expr) = ephemeral {
6734                self.write_space();
6735                self.generate_expression(expr)?;
6736            }
6737        }
6738
6739        // ClickHouse: MATERIALIZED expr
6740        if let Some(ref mat_expr) = col.materialized_expr {
6741            self.write_space();
6742            self.write_keyword("MATERIALIZED");
6743            self.write_space();
6744            self.generate_expression(mat_expr)?;
6745        }
6746
6747        // ClickHouse: ALIAS expr
6748        if let Some(ref alias_expr) = col.alias_expr {
6749            self.write_space();
6750            self.write_keyword("ALIAS");
6751            self.write_space();
6752            self.generate_expression(alias_expr)?;
6753        }
6754
6755        // ClickHouse: TTL expr
6756        if let Some(ref ttl_expr) = col.ttl_expr {
6757            self.write_space();
6758            self.write_keyword("TTL");
6759            self.write_space();
6760            self.generate_expression(ttl_expr)?;
6761        }
6762
6763        // TSQL: NOT FOR REPLICATION
6764        if col.not_for_replication && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)) {
6765            self.write_space();
6766            self.write_keyword("NOT FOR REPLICATION");
6767        }
6768
6769        // BigQuery: OPTIONS (key=value, ...) on column - comes after all constraints
6770        if !col.options.is_empty() {
6771            self.write_space();
6772            self.generate_options_clause(&col.options)?;
6773        }
6774
6775        // SQLite: Inline PRIMARY KEY from table constraint
6776        // This comes at the end, after all existing column constraints
6777        if !col.primary_key && self.sqlite_inline_pk_columns.contains(&col.name.name.to_lowercase()) {
6778            self.write_space();
6779            self.write_keyword("PRIMARY KEY");
6780        }
6781
6782        // SERIAL expansion: add GENERATED BY DEFAULT AS IDENTITY NOT NULL for PostgreSQL,
6783        // just NOT NULL for Materialize (which strips GENERATED AS IDENTITY)
6784        if serial_expansion.is_some() {
6785            if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) {
6786                self.write_space();
6787                self.write_keyword("GENERATED BY DEFAULT AS IDENTITY NOT NULL");
6788            } else if matches!(self.config.dialect, Some(DialectType::Materialize)) {
6789                self.write_space();
6790                self.write_keyword("NOT NULL");
6791            }
6792        }
6793
6794        Ok(())
6795    }
6796
6797    fn generate_table_constraint(&mut self, constraint: &TableConstraint) -> Result<()> {
6798        match constraint {
6799            TableConstraint::PrimaryKey { name, columns, include_columns, modifiers, has_constraint_keyword } => {
6800                if let Some(ref n) = name {
6801                    if *has_constraint_keyword {
6802                        self.write_keyword("CONSTRAINT");
6803                        self.write_space();
6804                        self.generate_identifier(n)?;
6805                        self.write_space();
6806                    }
6807                }
6808                self.write_keyword("PRIMARY KEY");
6809                // TSQL CLUSTERED/NONCLUSTERED modifier (before columns)
6810                if let Some(ref clustered) = modifiers.clustered {
6811                    self.write_space();
6812                    self.write_keyword(clustered);
6813                }
6814                // MySQL format: PRIMARY KEY name (cols) when no CONSTRAINT keyword
6815                if let Some(ref n) = name {
6816                    if !*has_constraint_keyword {
6817                        self.write_space();
6818                        self.generate_identifier(n)?;
6819                    }
6820                }
6821                self.write(" (");
6822                for (i, col) in columns.iter().enumerate() {
6823                    if i > 0 {
6824                        self.write(", ");
6825                    }
6826                    self.generate_identifier(col)?;
6827                }
6828                self.write(")");
6829                if !include_columns.is_empty() {
6830                    self.write_space();
6831                    self.write_keyword("INCLUDE");
6832                    self.write(" (");
6833                    for (i, col) in include_columns.iter().enumerate() {
6834                        if i > 0 {
6835                            self.write(", ");
6836                        }
6837                        self.generate_identifier(col)?;
6838                    }
6839                    self.write(")");
6840                }
6841                self.generate_constraint_modifiers(modifiers);
6842            }
6843            TableConstraint::Unique { name, columns, columns_parenthesized, modifiers, has_constraint_keyword, nulls_not_distinct } => {
6844                if let Some(ref n) = name {
6845                    if *has_constraint_keyword {
6846                        self.write_keyword("CONSTRAINT");
6847                        self.write_space();
6848                        self.generate_identifier(n)?;
6849                        self.write_space();
6850                    }
6851                }
6852                self.write_keyword("UNIQUE");
6853                // TSQL CLUSTERED/NONCLUSTERED modifier (before columns)
6854                if let Some(ref clustered) = modifiers.clustered {
6855                    self.write_space();
6856                    self.write_keyword(clustered);
6857                }
6858                // PostgreSQL 15+: NULLS NOT DISTINCT
6859                if *nulls_not_distinct {
6860                    self.write(" NULLS NOT DISTINCT");
6861                }
6862                // MySQL format: UNIQUE name (cols) when no CONSTRAINT keyword
6863                if let Some(ref n) = name {
6864                    if !*has_constraint_keyword {
6865                        self.write_space();
6866                        self.generate_identifier(n)?;
6867                    }
6868                }
6869                if *columns_parenthesized {
6870                    self.write(" (");
6871                    for (i, col) in columns.iter().enumerate() {
6872                        if i > 0 {
6873                            self.write(", ");
6874                        }
6875                        self.generate_identifier(col)?;
6876                    }
6877                    self.write(")");
6878                } else {
6879                    // UNIQUE without parentheses (e.g., UNIQUE idx_name)
6880                    for col in columns.iter() {
6881                        self.write_space();
6882                        self.generate_identifier(col)?;
6883                    }
6884                }
6885                self.generate_constraint_modifiers(modifiers);
6886            }
6887            TableConstraint::ForeignKey { name, columns, references, on_delete, on_update, modifiers } => {
6888                if let Some(ref n) = name {
6889                    self.write_keyword("CONSTRAINT");
6890                    self.write_space();
6891                    self.generate_identifier(n)?;
6892                    self.write_space();
6893                }
6894                self.write_keyword("FOREIGN KEY");
6895                self.write(" (");
6896                for (i, col) in columns.iter().enumerate() {
6897                    if i > 0 {
6898                        self.write(", ");
6899                    }
6900                    self.generate_identifier(col)?;
6901                }
6902                self.write(")");
6903                if let Some(ref refs) = references {
6904                    self.write(" ");
6905                    self.write_keyword("REFERENCES");
6906                    self.write_space();
6907                    self.generate_table(&refs.table)?;
6908                    if !refs.columns.is_empty() {
6909                        if self.config.pretty {
6910                            self.write(" (");
6911                            self.write_newline();
6912                            self.indent_level += 1;
6913                            for (i, col) in refs.columns.iter().enumerate() {
6914                                if i > 0 {
6915                                    self.write(",");
6916                                    self.write_newline();
6917                                }
6918                                self.write_indent();
6919                                self.generate_identifier(col)?;
6920                            }
6921                            self.indent_level -= 1;
6922                            self.write_newline();
6923                            self.write_indent();
6924                            self.write(")");
6925                        } else {
6926                            self.write(" (");
6927                            for (i, col) in refs.columns.iter().enumerate() {
6928                                if i > 0 {
6929                                    self.write(", ");
6930                                }
6931                                self.generate_identifier(col)?;
6932                            }
6933                            self.write(")");
6934                        }
6935                    }
6936                    self.generate_referential_actions(refs)?;
6937                } else {
6938                    // No REFERENCES - output ON DELETE/ON UPDATE directly
6939                    if let Some(ref action) = on_delete {
6940                        self.write_space();
6941                        self.write_keyword("ON DELETE");
6942                        self.write_space();
6943                        self.generate_referential_action(action);
6944                    }
6945                    if let Some(ref action) = on_update {
6946                        self.write_space();
6947                        self.write_keyword("ON UPDATE");
6948                        self.write_space();
6949                        self.generate_referential_action(action);
6950                    }
6951                }
6952                self.generate_constraint_modifiers(modifiers);
6953            }
6954            TableConstraint::Check { name, expression, modifiers } => {
6955                if let Some(ref n) = name {
6956                    self.write_keyword("CONSTRAINT");
6957                    self.write_space();
6958                    self.generate_identifier(n)?;
6959                    self.write_space();
6960                }
6961                self.write_keyword("CHECK");
6962                self.write(" (");
6963                self.generate_expression(expression)?;
6964                self.write(")");
6965                self.generate_constraint_modifiers(modifiers);
6966            }
6967            TableConstraint::Index { name, columns, kind, modifiers, use_key_keyword, expression, index_type, granularity } => {
6968                // ClickHouse-style INDEX: INDEX name expr TYPE type_func GRANULARITY n
6969                if expression.is_some() {
6970                    self.write_keyword("INDEX");
6971                    if let Some(ref n) = name {
6972                        self.write_space();
6973                        self.generate_identifier(n)?;
6974                    }
6975                    if let Some(ref expr) = expression {
6976                        self.write_space();
6977                        self.generate_expression(expr)?;
6978                    }
6979                    if let Some(ref idx_type) = index_type {
6980                        self.write_space();
6981                        self.write_keyword("TYPE");
6982                        self.write_space();
6983                        self.generate_expression(idx_type)?;
6984                    }
6985                    if let Some(ref gran) = granularity {
6986                        self.write_space();
6987                        self.write_keyword("GRANULARITY");
6988                        self.write_space();
6989                        self.generate_expression(gran)?;
6990                    }
6991                } else {
6992                    // Standard INDEX syntax
6993                    // Determine the index keyword to use
6994                    // MySQL normalizes KEY to INDEX
6995                    use crate::dialects::DialectType;
6996                    let index_keyword = if *use_key_keyword && !matches!(self.config.dialect, Some(DialectType::MySQL)) {
6997                        "KEY"
6998                    } else {
6999                        "INDEX"
7000                    };
7001
7002                    // Output kind (UNIQUE, FULLTEXT, SPATIAL) if present
7003                    if let Some(ref k) = kind {
7004                        self.write_keyword(k);
7005                        // For UNIQUE, don't add INDEX/KEY keyword
7006                        if k != "UNIQUE" {
7007                            self.write_space();
7008                            self.write_keyword(index_keyword);
7009                        }
7010                    } else {
7011                        self.write_keyword(index_keyword);
7012                    }
7013
7014                    // Output USING before name if using_before_columns is true and there's no name
7015                    if modifiers.using_before_columns && name.is_none() {
7016                        if let Some(ref using) = modifiers.using {
7017                            self.write_space();
7018                            self.write_keyword("USING");
7019                            self.write_space();
7020                            self.write_keyword(using);
7021                        }
7022                    }
7023
7024                    // Output index name if present
7025                    if let Some(ref n) = name {
7026                        self.write_space();
7027                        self.generate_identifier(n)?;
7028                    }
7029
7030                    // Output USING after name but before columns if using_before_columns and there's a name
7031                    if modifiers.using_before_columns && name.is_some() {
7032                        if let Some(ref using) = modifiers.using {
7033                            self.write_space();
7034                            self.write_keyword("USING");
7035                            self.write_space();
7036                            self.write_keyword(using);
7037                        }
7038                    }
7039
7040                    // Output columns
7041                    self.write(" (");
7042                    for (i, col) in columns.iter().enumerate() {
7043                        if i > 0 {
7044                            self.write(", ");
7045                        }
7046                        self.generate_identifier(col)?;
7047                    }
7048                    self.write(")");
7049
7050                    // Output USING after columns if not using_before_columns
7051                    if !modifiers.using_before_columns {
7052                        if let Some(ref using) = modifiers.using {
7053                            self.write_space();
7054                            self.write_keyword("USING");
7055                            self.write_space();
7056                            self.write_keyword(using);
7057                        }
7058                    }
7059
7060                    // Output other constraint modifiers (but skip USING since we already handled it)
7061                    self.generate_constraint_modifiers_without_using(modifiers);
7062                }
7063            }
7064            TableConstraint::Projection { name, expression } => {
7065                // ClickHouse: PROJECTION name (SELECT ...)
7066                self.write_keyword("PROJECTION");
7067                self.write_space();
7068                self.generate_identifier(name)?;
7069                self.write(" (");
7070                self.generate_expression(expression)?;
7071                self.write(")");
7072            }
7073            TableConstraint::Like { source, options } => {
7074                self.write_keyword("LIKE");
7075                self.write_space();
7076                self.generate_table(source)?;
7077                for (action, prop) in options {
7078                    self.write_space();
7079                    match action {
7080                        LikeOptionAction::Including => self.write_keyword("INCLUDING"),
7081                        LikeOptionAction::Excluding => self.write_keyword("EXCLUDING"),
7082                    }
7083                    self.write_space();
7084                    self.write_keyword(prop);
7085                }
7086            }
7087            TableConstraint::PeriodForSystemTime { start_col, end_col } => {
7088                self.write_keyword("PERIOD FOR SYSTEM_TIME");
7089                self.write(" (");
7090                self.generate_identifier(start_col)?;
7091                self.write(", ");
7092                self.generate_identifier(end_col)?;
7093                self.write(")");
7094            }
7095            TableConstraint::Exclude { name, using, elements, include_columns, where_clause, with_params, using_index_tablespace, modifiers: _ } => {
7096                if let Some(ref n) = name {
7097                    self.write_keyword("CONSTRAINT");
7098                    self.write_space();
7099                    self.generate_identifier(n)?;
7100                    self.write_space();
7101                }
7102                self.write_keyword("EXCLUDE");
7103                if let Some(ref method) = using {
7104                    self.write_space();
7105                    self.write_keyword("USING");
7106                    self.write_space();
7107                    self.write(method);
7108                    self.write("(");
7109                } else {
7110                    self.write(" (");
7111                }
7112                for (i, elem) in elements.iter().enumerate() {
7113                    if i > 0 {
7114                        self.write(", ");
7115                    }
7116                    self.write(&elem.expression);
7117                    self.write_space();
7118                    self.write_keyword("WITH");
7119                    self.write_space();
7120                    self.write(&elem.operator);
7121                }
7122                self.write(")");
7123                if !include_columns.is_empty() {
7124                    self.write_space();
7125                    self.write_keyword("INCLUDE");
7126                    self.write(" (");
7127                    for (i, col) in include_columns.iter().enumerate() {
7128                        if i > 0 {
7129                            self.write(", ");
7130                        }
7131                        self.generate_identifier(col)?;
7132                    }
7133                    self.write(")");
7134                }
7135                if !with_params.is_empty() {
7136                    self.write_space();
7137                    self.write_keyword("WITH");
7138                    self.write(" (");
7139                    for (i, (key, val)) in with_params.iter().enumerate() {
7140                        if i > 0 {
7141                            self.write(", ");
7142                        }
7143                        self.write(key);
7144                        self.write("=");
7145                        self.write(val);
7146                    }
7147                    self.write(")");
7148                }
7149                if let Some(ref tablespace) = using_index_tablespace {
7150                    self.write_space();
7151                    self.write_keyword("USING INDEX TABLESPACE");
7152                    self.write_space();
7153                    self.write(tablespace);
7154                }
7155                if let Some(ref where_expr) = where_clause {
7156                    self.write_space();
7157                    self.write_keyword("WHERE");
7158                    self.write(" (");
7159                    self.generate_expression(where_expr)?;
7160                    self.write(")");
7161                }
7162            }
7163            TableConstraint::Tags(tags) => {
7164                self.write_keyword("TAG");
7165                self.write(" (");
7166                for (i, expr) in tags.expressions.iter().enumerate() {
7167                    if i > 0 {
7168                        self.write(", ");
7169                    }
7170                    self.generate_expression(expr)?;
7171                }
7172                self.write(")");
7173            }
7174            TableConstraint::InitiallyDeferred { deferred } => {
7175                self.write_keyword("INITIALLY");
7176                self.write_space();
7177                if *deferred {
7178                    self.write_keyword("DEFERRED");
7179                } else {
7180                    self.write_keyword("IMMEDIATE");
7181                }
7182            }
7183        }
7184        Ok(())
7185    }
7186
7187    fn generate_constraint_modifiers(&mut self, modifiers: &ConstraintModifiers) {
7188        // Output USING BTREE/HASH (MySQL) - comes first
7189        if let Some(using) = &modifiers.using {
7190            self.write_space();
7191            self.write_keyword("USING");
7192            self.write_space();
7193            self.write_keyword(using);
7194        }
7195        // Output ENFORCED/NOT ENFORCED
7196        if let Some(enforced) = modifiers.enforced {
7197            self.write_space();
7198            if enforced {
7199                self.write_keyword("ENFORCED");
7200            } else {
7201                self.write_keyword("NOT ENFORCED");
7202            }
7203        }
7204        // Output DEFERRABLE/NOT DEFERRABLE
7205        if let Some(deferrable) = modifiers.deferrable {
7206            self.write_space();
7207            if deferrable {
7208                self.write_keyword("DEFERRABLE");
7209            } else {
7210                self.write_keyword("NOT DEFERRABLE");
7211            }
7212        }
7213        // Output INITIALLY DEFERRED/INITIALLY IMMEDIATE
7214        if let Some(initially_deferred) = modifiers.initially_deferred {
7215            self.write_space();
7216            if initially_deferred {
7217                self.write_keyword("INITIALLY DEFERRED");
7218            } else {
7219                self.write_keyword("INITIALLY IMMEDIATE");
7220            }
7221        }
7222        // Output NORELY
7223        if modifiers.norely {
7224            self.write_space();
7225            self.write_keyword("NORELY");
7226        }
7227        // Output RELY
7228        if modifiers.rely {
7229            self.write_space();
7230            self.write_keyword("RELY");
7231        }
7232        // Output NOT VALID (PostgreSQL)
7233        if modifiers.not_valid {
7234            self.write_space();
7235            self.write_keyword("NOT VALID");
7236        }
7237        // Output ON CONFLICT (SQLite)
7238        if let Some(on_conflict) = &modifiers.on_conflict {
7239            self.write_space();
7240            self.write_keyword("ON CONFLICT");
7241            self.write_space();
7242            self.write_keyword(on_conflict);
7243        }
7244        // Output TSQL WITH options (PAD_INDEX=ON, STATISTICS_NORECOMPUTE=OFF, ...)
7245        if !modifiers.with_options.is_empty() {
7246            self.write_space();
7247            self.write_keyword("WITH");
7248            self.write(" (");
7249            for (i, (key, value)) in modifiers.with_options.iter().enumerate() {
7250                if i > 0 {
7251                    self.write(", ");
7252                }
7253                self.write(key);
7254                self.write("=");
7255                self.write(value);
7256            }
7257            self.write(")");
7258        }
7259        // Output TSQL ON filegroup
7260        if let Some(ref fg) = modifiers.on_filegroup {
7261            self.write_space();
7262            self.write_keyword("ON");
7263            self.write_space();
7264            let _ = self.generate_identifier(fg);
7265        }
7266    }
7267
7268    /// Generate constraint modifiers without USING (for Index constraints where USING is handled separately)
7269    fn generate_constraint_modifiers_without_using(&mut self, modifiers: &ConstraintModifiers) {
7270        // Output ENFORCED/NOT ENFORCED
7271        if let Some(enforced) = modifiers.enforced {
7272            self.write_space();
7273            if enforced {
7274                self.write_keyword("ENFORCED");
7275            } else {
7276                self.write_keyword("NOT ENFORCED");
7277            }
7278        }
7279        // Output DEFERRABLE/NOT DEFERRABLE
7280        if let Some(deferrable) = modifiers.deferrable {
7281            self.write_space();
7282            if deferrable {
7283                self.write_keyword("DEFERRABLE");
7284            } else {
7285                self.write_keyword("NOT DEFERRABLE");
7286            }
7287        }
7288        // Output INITIALLY DEFERRED/INITIALLY IMMEDIATE
7289        if let Some(initially_deferred) = modifiers.initially_deferred {
7290            self.write_space();
7291            if initially_deferred {
7292                self.write_keyword("INITIALLY DEFERRED");
7293            } else {
7294                self.write_keyword("INITIALLY IMMEDIATE");
7295            }
7296        }
7297        // Output NORELY
7298        if modifiers.norely {
7299            self.write_space();
7300            self.write_keyword("NORELY");
7301        }
7302        // Output RELY
7303        if modifiers.rely {
7304            self.write_space();
7305            self.write_keyword("RELY");
7306        }
7307        // Output NOT VALID (PostgreSQL)
7308        if modifiers.not_valid {
7309            self.write_space();
7310            self.write_keyword("NOT VALID");
7311        }
7312        // Output ON CONFLICT (SQLite)
7313        if let Some(on_conflict) = &modifiers.on_conflict {
7314            self.write_space();
7315            self.write_keyword("ON CONFLICT");
7316            self.write_space();
7317            self.write_keyword(on_conflict);
7318        }
7319        // Output MySQL index-specific modifiers
7320        self.generate_index_specific_modifiers(modifiers);
7321    }
7322
7323    /// Generate MySQL index-specific modifiers (COMMENT, VISIBLE, ENGINE_ATTRIBUTE, WITH PARSER)
7324    fn generate_index_specific_modifiers(&mut self, modifiers: &ConstraintModifiers) {
7325        if let Some(ref comment) = modifiers.comment {
7326            self.write_space();
7327            self.write_keyword("COMMENT");
7328            self.write(" '");
7329            self.write(comment);
7330            self.write("'");
7331        }
7332        if let Some(visible) = modifiers.visible {
7333            self.write_space();
7334            if visible {
7335                self.write_keyword("VISIBLE");
7336            } else {
7337                self.write_keyword("INVISIBLE");
7338            }
7339        }
7340        if let Some(ref attr) = modifiers.engine_attribute {
7341            self.write_space();
7342            self.write_keyword("ENGINE_ATTRIBUTE");
7343            self.write(" = '");
7344            self.write(attr);
7345            self.write("'");
7346        }
7347        if let Some(ref parser) = modifiers.with_parser {
7348            self.write_space();
7349            self.write_keyword("WITH PARSER");
7350            self.write_space();
7351            self.write(parser);
7352        }
7353    }
7354
7355    fn generate_referential_actions(&mut self, fk_ref: &ForeignKeyRef) -> Result<()> {
7356        // MATCH clause before ON DELETE/ON UPDATE (default position, e.g. PostgreSQL)
7357        if !fk_ref.match_after_actions {
7358            if let Some(ref match_type) = fk_ref.match_type {
7359                self.write_space();
7360                self.write_keyword("MATCH");
7361                self.write_space();
7362                match match_type {
7363                    MatchType::Full => self.write_keyword("FULL"),
7364                    MatchType::Partial => self.write_keyword("PARTIAL"),
7365                    MatchType::Simple => self.write_keyword("SIMPLE"),
7366                }
7367            }
7368        }
7369
7370        // Output ON UPDATE and ON DELETE in the original order
7371        if fk_ref.on_update_first {
7372            if let Some(ref action) = fk_ref.on_update {
7373                self.write_space();
7374                self.write_keyword("ON UPDATE");
7375                self.write_space();
7376                self.generate_referential_action(action);
7377            }
7378            if let Some(ref action) = fk_ref.on_delete {
7379                self.write_space();
7380                self.write_keyword("ON DELETE");
7381                self.write_space();
7382                self.generate_referential_action(action);
7383            }
7384        } else {
7385            if let Some(ref action) = fk_ref.on_delete {
7386                self.write_space();
7387                self.write_keyword("ON DELETE");
7388                self.write_space();
7389                self.generate_referential_action(action);
7390            }
7391            if let Some(ref action) = fk_ref.on_update {
7392                self.write_space();
7393                self.write_keyword("ON UPDATE");
7394                self.write_space();
7395                self.generate_referential_action(action);
7396            }
7397        }
7398
7399        // MATCH clause after ON DELETE/ON UPDATE (when original SQL had it after)
7400        if fk_ref.match_after_actions {
7401            if let Some(ref match_type) = fk_ref.match_type {
7402                self.write_space();
7403                self.write_keyword("MATCH");
7404                self.write_space();
7405                match match_type {
7406                    MatchType::Full => self.write_keyword("FULL"),
7407                    MatchType::Partial => self.write_keyword("PARTIAL"),
7408                    MatchType::Simple => self.write_keyword("SIMPLE"),
7409                }
7410            }
7411        }
7412
7413        // DEFERRABLE / NOT DEFERRABLE
7414        if let Some(deferrable) = fk_ref.deferrable {
7415            self.write_space();
7416            if deferrable {
7417                self.write_keyword("DEFERRABLE");
7418            } else {
7419                self.write_keyword("NOT DEFERRABLE");
7420            }
7421        }
7422
7423        Ok(())
7424    }
7425
7426    fn generate_referential_action(&mut self, action: &ReferentialAction) {
7427        match action {
7428            ReferentialAction::Cascade => self.write_keyword("CASCADE"),
7429            ReferentialAction::SetNull => self.write_keyword("SET NULL"),
7430            ReferentialAction::SetDefault => self.write_keyword("SET DEFAULT"),
7431            ReferentialAction::Restrict => self.write_keyword("RESTRICT"),
7432            ReferentialAction::NoAction => self.write_keyword("NO ACTION"),
7433        }
7434    }
7435
7436    fn generate_drop_table(&mut self, dt: &DropTable) -> Result<()> {
7437        // Athena: DROP TABLE uses Hive engine (backticks)
7438        let saved_athena_hive_context = self.athena_hive_context;
7439        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
7440            self.athena_hive_context = true;
7441        }
7442
7443        self.write_keyword("DROP TABLE");
7444
7445        if dt.if_exists {
7446            self.write_space();
7447            self.write_keyword("IF EXISTS");
7448        }
7449
7450        self.write_space();
7451        for (i, table) in dt.names.iter().enumerate() {
7452            if i > 0 {
7453                self.write(", ");
7454            }
7455            self.generate_table(table)?;
7456        }
7457
7458        if dt.cascade_constraints {
7459            self.write_space();
7460            self.write_keyword("CASCADE CONSTRAINTS");
7461        } else if dt.cascade {
7462            self.write_space();
7463            self.write_keyword("CASCADE");
7464        }
7465
7466        if dt.purge {
7467            self.write_space();
7468            self.write_keyword("PURGE");
7469        }
7470
7471        // Restore Athena Hive context
7472        self.athena_hive_context = saved_athena_hive_context;
7473
7474        Ok(())
7475    }
7476
7477    fn generate_alter_table(&mut self, at: &AlterTable) -> Result<()> {
7478        // Athena: ALTER TABLE uses Hive engine (backticks)
7479        let saved_athena_hive_context = self.athena_hive_context;
7480        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
7481            self.athena_hive_context = true;
7482        }
7483
7484        self.write_keyword("ALTER TABLE");
7485        if at.if_exists {
7486            self.write_space();
7487            self.write_keyword("IF EXISTS");
7488        }
7489        self.write_space();
7490        self.generate_table(&at.name)?;
7491
7492        // ClickHouse: ON CLUSTER clause
7493        if let Some(ref on_cluster) = at.on_cluster {
7494            self.write_space();
7495            self.generate_on_cluster(on_cluster)?;
7496        }
7497
7498        // Hive: PARTITION(key=value, ...) clause
7499        if let Some(ref partition) = at.partition {
7500            self.write_space();
7501            self.write_keyword("PARTITION");
7502            self.write("(");
7503            for (i, (key, value)) in partition.iter().enumerate() {
7504                if i > 0 {
7505                    self.write(", ");
7506                }
7507                self.generate_identifier(key)?;
7508                self.write(" = ");
7509                self.generate_expression(value)?;
7510            }
7511            self.write(")");
7512        }
7513
7514        // TSQL: WITH CHECK / WITH NOCHECK modifier
7515        if let Some(ref with_check) = at.with_check {
7516            self.write_space();
7517            self.write_keyword(with_check);
7518        }
7519
7520        if self.config.pretty {
7521            // In pretty mode, format actions with newlines and indentation
7522            self.write_newline();
7523            self.indent_level += 1;
7524            for (i, action) in at.actions.iter().enumerate() {
7525                // Check if this is a continuation of previous ADD COLUMN or ADD CONSTRAINT
7526                let is_continuation = i > 0 && matches!(
7527                    (&at.actions[i - 1], action),
7528                    (AlterTableAction::AddColumn { .. }, AlterTableAction::AddColumn { .. })
7529                    | (AlterTableAction::AddConstraint(_), AlterTableAction::AddConstraint(_))
7530                );
7531                if i > 0 {
7532                    self.write(",");
7533                    self.write_newline();
7534                }
7535                self.write_indent();
7536                self.generate_alter_action_with_continuation(action, is_continuation)?;
7537            }
7538            self.indent_level -= 1;
7539        } else {
7540            for (i, action) in at.actions.iter().enumerate() {
7541                // Check if this is a continuation of previous ADD COLUMN or ADD CONSTRAINT
7542                let is_continuation = i > 0 && matches!(
7543                    (&at.actions[i - 1], action),
7544                    (AlterTableAction::AddColumn { .. }, AlterTableAction::AddColumn { .. })
7545                    | (AlterTableAction::AddConstraint(_), AlterTableAction::AddConstraint(_))
7546                );
7547                if i > 0 {
7548                    self.write(",");
7549                }
7550                self.write_space();
7551                self.generate_alter_action_with_continuation(action, is_continuation)?;
7552            }
7553        }
7554
7555        // MySQL ALTER TABLE trailing options
7556        if let Some(ref algorithm) = at.algorithm {
7557            self.write(", ");
7558            self.write_keyword("ALGORITHM");
7559            self.write("=");
7560            self.write_keyword(algorithm);
7561        }
7562        if let Some(ref lock) = at.lock {
7563            self.write(", ");
7564            self.write_keyword("LOCK");
7565            self.write("=");
7566            self.write_keyword(lock);
7567        }
7568
7569        // Restore Athena Hive context
7570        self.athena_hive_context = saved_athena_hive_context;
7571
7572        Ok(())
7573    }
7574
7575    fn generate_alter_action_with_continuation(&mut self, action: &AlterTableAction, is_continuation: bool) -> Result<()> {
7576        match action {
7577            AlterTableAction::AddColumn { column, if_not_exists, position } => {
7578                use crate::dialects::DialectType;
7579                // For Snowflake: consecutive ADD COLUMN actions are combined with commas
7580                // e.g., "ADD col1, col2" instead of "ADD col1, ADD col2"
7581                // For other dialects, repeat ADD COLUMN for each
7582                let is_snowflake = matches!(self.config.dialect, Some(DialectType::Snowflake));
7583                let is_tsql_like = matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric));
7584                // Athena uses "ADD COLUMNS (col_def)" instead of "ADD COLUMN col_def"
7585                let is_athena = matches!(self.config.dialect, Some(DialectType::Athena));
7586
7587                if is_continuation && (is_snowflake || is_tsql_like) {
7588                    // Don't write ADD keyword for continuation in Snowflake/TSQL
7589                } else if is_snowflake {
7590                    self.write_keyword("ADD");
7591                    self.write_space();
7592                } else if is_athena {
7593                    // Athena uses ADD COLUMNS (col_def) syntax
7594                    self.write_keyword("ADD COLUMNS");
7595                    self.write(" (");
7596                } else if self.config.alter_table_include_column_keyword {
7597                    self.write_keyword("ADD COLUMN");
7598                    self.write_space();
7599                } else {
7600                    // Dialects like Oracle and TSQL don't use COLUMN keyword
7601                    self.write_keyword("ADD");
7602                    self.write_space();
7603                }
7604
7605                if *if_not_exists {
7606                    self.write_keyword("IF NOT EXISTS");
7607                    self.write_space();
7608                }
7609                self.generate_column_def(column)?;
7610
7611                // Close parenthesis for Athena
7612                if is_athena {
7613                    self.write(")");
7614                }
7615
7616                // Column position (FIRST or AFTER)
7617                if let Some(pos) = position {
7618                    self.write_space();
7619                    match pos {
7620                        ColumnPosition::First => self.write_keyword("FIRST"),
7621                        ColumnPosition::After(col_name) => {
7622                            self.write_keyword("AFTER");
7623                            self.write_space();
7624                            self.generate_identifier(col_name)?;
7625                        }
7626                    }
7627                }
7628            }
7629            AlterTableAction::DropColumn { name, if_exists, cascade } => {
7630                self.write_keyword("DROP COLUMN");
7631                if *if_exists {
7632                    self.write_space();
7633                    self.write_keyword("IF EXISTS");
7634                }
7635                self.write_space();
7636                self.generate_identifier(name)?;
7637                if *cascade {
7638                    self.write_space();
7639                    self.write_keyword("CASCADE");
7640                }
7641            }
7642            AlterTableAction::DropColumns { names } => {
7643                self.write_keyword("DROP COLUMNS");
7644                self.write(" (");
7645                for (i, name) in names.iter().enumerate() {
7646                    if i > 0 {
7647                        self.write(", ");
7648                    }
7649                    self.generate_identifier(name)?;
7650                }
7651                self.write(")");
7652            }
7653            AlterTableAction::RenameColumn { old_name, new_name, if_exists } => {
7654                self.write_keyword("RENAME COLUMN");
7655                if *if_exists {
7656                    self.write_space();
7657                    self.write_keyword("IF EXISTS");
7658                }
7659                self.write_space();
7660                self.generate_identifier(old_name)?;
7661                self.write_space();
7662                self.write_keyword("TO");
7663                self.write_space();
7664                self.generate_identifier(new_name)?;
7665            }
7666            AlterTableAction::AlterColumn { name, action, use_modify_keyword } => {
7667                use crate::dialects::DialectType;
7668                // MySQL uses MODIFY COLUMN for type changes (SetDataType)
7669                // but ALTER COLUMN for SET DEFAULT, DROP DEFAULT, etc.
7670                let use_modify = *use_modify_keyword || (
7671                    matches!(self.config.dialect, Some(DialectType::MySQL)) &&
7672                    matches!(action, AlterColumnAction::SetDataType { .. })
7673                );
7674                if use_modify {
7675                    self.write_keyword("MODIFY COLUMN");
7676                    self.write_space();
7677                    self.generate_identifier(name)?;
7678                    // For MODIFY COLUMN, output the type directly
7679                    if let AlterColumnAction::SetDataType { data_type, using: _, collate } = action {
7680                        self.write_space();
7681                        self.generate_data_type(data_type)?;
7682                        // Output COLLATE clause if present
7683                        if let Some(collate_name) = collate {
7684                            self.write_space();
7685                            self.write_keyword("COLLATE");
7686                            self.write_space();
7687                            // Output as single-quoted string
7688                            self.write(&format!("'{}'", collate_name));
7689                        }
7690                    } else {
7691                        self.write_space();
7692                        self.generate_alter_column_action(action)?;
7693                    }
7694                } else if matches!(self.config.dialect, Some(DialectType::Hive))
7695                    && matches!(action, AlterColumnAction::SetDataType { .. })
7696                {
7697                    // Hive uses CHANGE COLUMN col_name col_name NEW_TYPE
7698                    self.write_keyword("CHANGE COLUMN");
7699                    self.write_space();
7700                    self.generate_identifier(name)?;
7701                    self.write_space();
7702                    self.generate_identifier(name)?;
7703                    if let AlterColumnAction::SetDataType { data_type, .. } = action {
7704                        self.write_space();
7705                        self.generate_data_type(data_type)?;
7706                    }
7707                } else {
7708                    self.write_keyword("ALTER COLUMN");
7709                    self.write_space();
7710                    self.generate_identifier(name)?;
7711                    self.write_space();
7712                    self.generate_alter_column_action(action)?;
7713                }
7714            }
7715            AlterTableAction::RenameTable(new_name) => {
7716                // MySQL-like dialects (MySQL, Doris, StarRocks) use RENAME without TO
7717                let mysql_like = matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::Doris) | Some(DialectType::StarRocks) | Some(DialectType::SingleStore));
7718                if mysql_like {
7719                    self.write_keyword("RENAME");
7720                } else {
7721                    self.write_keyword("RENAME TO");
7722                }
7723                self.write_space();
7724                // Doris, DuckDB, BigQuery, PostgreSQL strip schema/catalog from target table
7725                let rename_table_with_db = !matches!(self.config.dialect, Some(DialectType::Doris) | Some(DialectType::DuckDB) | Some(DialectType::BigQuery) | Some(DialectType::PostgreSQL));
7726                if !rename_table_with_db {
7727                    let mut stripped = new_name.clone();
7728                    stripped.schema = None;
7729                    stripped.catalog = None;
7730                    self.generate_table(&stripped)?;
7731                } else {
7732                    self.generate_table(new_name)?;
7733                }
7734            }
7735            AlterTableAction::AddConstraint(constraint) => {
7736                // For consecutive ADD CONSTRAINT actions (is_continuation=true), skip ADD keyword
7737                // to produce: ADD CONSTRAINT c1 ..., CONSTRAINT c2 ...
7738                if !is_continuation {
7739                    self.write_keyword("ADD");
7740                    self.write_space();
7741                }
7742                self.generate_table_constraint(constraint)?;
7743            }
7744            AlterTableAction::DropConstraint { name, if_exists } => {
7745                self.write_keyword("DROP CONSTRAINT");
7746                if *if_exists {
7747                    self.write_space();
7748                    self.write_keyword("IF EXISTS");
7749                }
7750                self.write_space();
7751                self.generate_identifier(name)?;
7752            }
7753            AlterTableAction::DropForeignKey { name } => {
7754                self.write_keyword("DROP FOREIGN KEY");
7755                self.write_space();
7756                self.generate_identifier(name)?;
7757            }
7758            AlterTableAction::DropPartition { partitions, if_exists } => {
7759                self.write_keyword("DROP");
7760                if *if_exists {
7761                    self.write_space();
7762                    self.write_keyword("IF EXISTS");
7763                }
7764                for (i, partition) in partitions.iter().enumerate() {
7765                    if i > 0 {
7766                        self.write(",");
7767                    }
7768                    self.write_space();
7769                    self.write_keyword("PARTITION");
7770                    // Check for special ClickHouse partition formats
7771                    if partition.len() == 1 && partition[0].0.name == "__expr__" {
7772                        // ClickHouse: PARTITION <expression>
7773                        self.write_space();
7774                        self.generate_expression(&partition[0].1)?;
7775                    } else if partition.len() == 1 && partition[0].0.name == "ALL" {
7776                        // ClickHouse: PARTITION ALL
7777                        self.write_space();
7778                        self.write_keyword("ALL");
7779                    } else if partition.len() == 1 && partition[0].0.name == "ID" {
7780                        // ClickHouse: PARTITION ID 'string'
7781                        self.write_space();
7782                        self.write_keyword("ID");
7783                        self.write_space();
7784                        self.generate_expression(&partition[0].1)?;
7785                    } else {
7786                        // Standard SQL: PARTITION(key=value, ...)
7787                        self.write("(");
7788                        for (j, (key, value)) in partition.iter().enumerate() {
7789                            if j > 0 {
7790                                self.write(", ");
7791                            }
7792                            self.generate_identifier(key)?;
7793                            self.write(" = ");
7794                            self.generate_expression(value)?;
7795                        }
7796                        self.write(")");
7797                    }
7798                }
7799            }
7800            AlterTableAction::Delete { where_clause } => {
7801                self.write_keyword("DELETE");
7802                self.write_space();
7803                self.write_keyword("WHERE");
7804                self.write_space();
7805                self.generate_expression(where_clause)?;
7806            }
7807            AlterTableAction::SwapWith(target) => {
7808                self.write_keyword("SWAP WITH");
7809                self.write_space();
7810                self.generate_table(target)?;
7811            }
7812            AlterTableAction::SetProperty { properties } => {
7813                use crate::dialects::DialectType;
7814                self.write_keyword("SET");
7815                // Trino/Presto use SET PROPERTIES syntax with spaces around =
7816                let is_trino_presto = matches!(self.config.dialect, Some(DialectType::Trino) | Some(DialectType::Presto));
7817                if is_trino_presto {
7818                    self.write_space();
7819                    self.write_keyword("PROPERTIES");
7820                }
7821                let eq = if is_trino_presto { " = " } else { "=" };
7822                for (i, (key, value)) in properties.iter().enumerate() {
7823                    if i > 0 {
7824                        self.write(",");
7825                    }
7826                    self.write_space();
7827                    // Handle quoted property names for Trino
7828                    if key.contains(' ') {
7829                        self.generate_string_literal(key)?;
7830                    } else {
7831                        self.write(key);
7832                    }
7833                    self.write(eq);
7834                    self.generate_expression(value)?;
7835                }
7836            }
7837            AlterTableAction::UnsetProperty { properties } => {
7838                self.write_keyword("UNSET");
7839                for (i, name) in properties.iter().enumerate() {
7840                    if i > 0 {
7841                        self.write(",");
7842                    }
7843                    self.write_space();
7844                    self.write(name);
7845                }
7846            }
7847            AlterTableAction::ClusterBy { expressions } => {
7848                self.write_keyword("CLUSTER BY");
7849                self.write(" (");
7850                for (i, expr) in expressions.iter().enumerate() {
7851                    if i > 0 {
7852                        self.write(", ");
7853                    }
7854                    self.generate_expression(expr)?;
7855                }
7856                self.write(")");
7857            }
7858            AlterTableAction::SetTag { expressions } => {
7859                self.write_keyword("SET TAG");
7860                for (i, (key, value)) in expressions.iter().enumerate() {
7861                    if i > 0 {
7862                        self.write(",");
7863                    }
7864                    self.write_space();
7865                    self.write(key);
7866                    self.write(" = ");
7867                    self.generate_expression(value)?;
7868                }
7869            }
7870            AlterTableAction::UnsetTag { names } => {
7871                self.write_keyword("UNSET TAG");
7872                for (i, name) in names.iter().enumerate() {
7873                    if i > 0 {
7874                        self.write(",");
7875                    }
7876                    self.write_space();
7877                    self.write(name);
7878                }
7879            }
7880            AlterTableAction::SetOptions { expressions } => {
7881                self.write_keyword("SET");
7882                self.write(" (");
7883                for (i, expr) in expressions.iter().enumerate() {
7884                    if i > 0 {
7885                        self.write(", ");
7886                    }
7887                    self.generate_expression(expr)?;
7888                }
7889                self.write(")");
7890            }
7891            AlterTableAction::AlterIndex { name, visible } => {
7892                self.write_keyword("ALTER INDEX");
7893                self.write_space();
7894                self.generate_identifier(name)?;
7895                self.write_space();
7896                if *visible {
7897                    self.write_keyword("VISIBLE");
7898                } else {
7899                    self.write_keyword("INVISIBLE");
7900                }
7901            }
7902            AlterTableAction::SetAttribute { attribute } => {
7903                self.write_keyword("SET");
7904                self.write_space();
7905                self.write_keyword(attribute);
7906            }
7907            AlterTableAction::SetStageFileFormat { options } => {
7908                self.write_keyword("SET");
7909                self.write_space();
7910                self.write_keyword("STAGE_FILE_FORMAT");
7911                self.write(" = (");
7912                if let Some(opts) = options {
7913                    self.generate_space_separated_properties(opts)?;
7914                }
7915                self.write(")");
7916            }
7917            AlterTableAction::SetStageCopyOptions { options } => {
7918                self.write_keyword("SET");
7919                self.write_space();
7920                self.write_keyword("STAGE_COPY_OPTIONS");
7921                self.write(" = (");
7922                if let Some(opts) = options {
7923                    self.generate_space_separated_properties(opts)?;
7924                }
7925                self.write(")");
7926            }
7927            AlterTableAction::AddColumns { columns, cascade } => {
7928                // Oracle uses ADD (...) without COLUMNS keyword
7929                // Hive/Spark uses ADD COLUMNS (...)
7930                let is_oracle = matches!(self.config.dialect, Some(DialectType::Oracle));
7931                if is_oracle {
7932                    self.write_keyword("ADD");
7933                } else {
7934                    self.write_keyword("ADD COLUMNS");
7935                }
7936                self.write(" (");
7937                for (i, col) in columns.iter().enumerate() {
7938                    if i > 0 {
7939                        self.write(", ");
7940                    }
7941                    self.generate_column_def(col)?;
7942                }
7943                self.write(")");
7944                if *cascade {
7945                    self.write_space();
7946                    self.write_keyword("CASCADE");
7947                }
7948            }
7949            AlterTableAction::ChangeColumn { old_name, new_name, data_type, comment, cascade } => {
7950                use crate::dialects::DialectType;
7951                let is_spark = matches!(self.config.dialect,
7952                    Some(DialectType::Spark) | Some(DialectType::Databricks));
7953                let is_rename = old_name.name != new_name.name;
7954
7955                if is_spark {
7956                    if is_rename {
7957                        // Spark: RENAME COLUMN old TO new
7958                        self.write_keyword("RENAME COLUMN");
7959                        self.write_space();
7960                        self.generate_identifier(old_name)?;
7961                        self.write_space();
7962                        self.write_keyword("TO");
7963                        self.write_space();
7964                        self.generate_identifier(new_name)?;
7965                    } else if comment.is_some() {
7966                        // Spark: ALTER COLUMN old COMMENT 'comment'
7967                        self.write_keyword("ALTER COLUMN");
7968                        self.write_space();
7969                        self.generate_identifier(old_name)?;
7970                        self.write_space();
7971                        self.write_keyword("COMMENT");
7972                        self.write_space();
7973                        self.write("'");
7974                        self.write(comment.as_ref().unwrap());
7975                        self.write("'");
7976                    } else if data_type.is_some() {
7977                        // Spark: ALTER COLUMN old TYPE data_type
7978                        self.write_keyword("ALTER COLUMN");
7979                        self.write_space();
7980                        self.generate_identifier(old_name)?;
7981                        self.write_space();
7982                        self.write_keyword("TYPE");
7983                        self.write_space();
7984                        self.generate_data_type(data_type.as_ref().unwrap())?;
7985                    } else {
7986                        // Fallback to CHANGE COLUMN
7987                        self.write_keyword("CHANGE COLUMN");
7988                        self.write_space();
7989                        self.generate_identifier(old_name)?;
7990                        self.write_space();
7991                        self.generate_identifier(new_name)?;
7992                    }
7993                } else {
7994                    // Hive/MySQL/default: CHANGE [COLUMN] old new [type] [COMMENT '...'] [CASCADE]
7995                    if data_type.is_some() {
7996                        self.write_keyword("CHANGE COLUMN");
7997                    } else {
7998                        self.write_keyword("CHANGE");
7999                    }
8000                    self.write_space();
8001                    self.generate_identifier(old_name)?;
8002                    self.write_space();
8003                    self.generate_identifier(new_name)?;
8004                    if let Some(ref dt) = data_type {
8005                        self.write_space();
8006                        self.generate_data_type(dt)?;
8007                    }
8008                    if let Some(ref c) = comment {
8009                        self.write_space();
8010                        self.write_keyword("COMMENT");
8011                        self.write_space();
8012                        self.write("'");
8013                        self.write(c);
8014                        self.write("'");
8015                    }
8016                    if *cascade {
8017                        self.write_space();
8018                        self.write_keyword("CASCADE");
8019                    }
8020                }
8021            }
8022            AlterTableAction::AddPartition { partition, if_not_exists, location } => {
8023                self.write_keyword("ADD");
8024                self.write_space();
8025                if *if_not_exists {
8026                    self.write_keyword("IF NOT EXISTS");
8027                    self.write_space();
8028                }
8029                self.generate_expression(partition)?;
8030                if let Some(ref loc) = location {
8031                    self.write_space();
8032                    self.write_keyword("LOCATION");
8033                    self.write_space();
8034                    self.generate_expression(loc)?;
8035                }
8036            }
8037            AlterTableAction::AlterSortKey { this, expressions, compound } => {
8038                // Redshift: ALTER [COMPOUND] SORTKEY AUTO|NONE|(col1, col2)
8039                self.write_keyword("ALTER");
8040                if *compound {
8041                    self.write_space();
8042                    self.write_keyword("COMPOUND");
8043                }
8044                self.write_space();
8045                self.write_keyword("SORTKEY");
8046                self.write_space();
8047                if let Some(style) = this {
8048                    self.write_keyword(style);
8049                } else if !expressions.is_empty() {
8050                    self.write("(");
8051                    for (i, expr) in expressions.iter().enumerate() {
8052                        if i > 0 {
8053                            self.write(", ");
8054                        }
8055                        self.generate_expression(expr)?;
8056                    }
8057                    self.write(")");
8058                }
8059            }
8060            AlterTableAction::AlterDistStyle { style, distkey } => {
8061                // Redshift: ALTER DISTSTYLE ALL|EVEN|AUTO|KEY [DISTKEY col]
8062                self.write_keyword("ALTER");
8063                self.write_space();
8064                self.write_keyword("DISTSTYLE");
8065                self.write_space();
8066                self.write_keyword(style);
8067                if let Some(col) = distkey {
8068                    self.write_space();
8069                    self.write_keyword("DISTKEY");
8070                    self.write_space();
8071                    self.generate_identifier(col)?;
8072                }
8073            }
8074            AlterTableAction::SetTableProperties { properties } => {
8075                // Redshift: SET TABLE PROPERTIES ('a' = '5', 'b' = 'c')
8076                self.write_keyword("SET TABLE PROPERTIES");
8077                self.write(" (");
8078                for (i, (key, value)) in properties.iter().enumerate() {
8079                    if i > 0 {
8080                        self.write(", ");
8081                    }
8082                    self.generate_expression(key)?;
8083                    self.write(" = ");
8084                    self.generate_expression(value)?;
8085                }
8086                self.write(")");
8087            }
8088            AlterTableAction::SetLocation { location } => {
8089                // Redshift: SET LOCATION 's3://bucket/folder/'
8090                self.write_keyword("SET LOCATION");
8091                self.write_space();
8092                self.write("'");
8093                self.write(location);
8094                self.write("'");
8095            }
8096            AlterTableAction::SetFileFormat { format } => {
8097                // Redshift: SET FILE FORMAT AVRO
8098                self.write_keyword("SET FILE FORMAT");
8099                self.write_space();
8100                self.write_keyword(format);
8101            }
8102            AlterTableAction::ReplacePartition { partition, source } => {
8103                // ClickHouse: REPLACE PARTITION expr FROM source
8104                self.write_keyword("REPLACE PARTITION");
8105                self.write_space();
8106                self.generate_expression(partition)?;
8107                if let Some(src) = source {
8108                    self.write_space();
8109                    self.write_keyword("FROM");
8110                    self.write_space();
8111                    self.generate_expression(src)?;
8112                }
8113            }
8114        }
8115        Ok(())
8116    }
8117
8118    fn generate_alter_column_action(&mut self, action: &AlterColumnAction) -> Result<()> {
8119        match action {
8120            AlterColumnAction::SetDataType { data_type, using, collate } => {
8121                use crate::dialects::DialectType;
8122                // Dialect-specific type change syntax:
8123                // - TSQL/Fabric/Hive: no prefix (ALTER COLUMN col datatype)
8124                // - Redshift/Spark: TYPE (ALTER COLUMN col TYPE datatype)
8125                // - Default: SET DATA TYPE (ALTER COLUMN col SET DATA TYPE datatype)
8126                let is_no_prefix = matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric) | Some(DialectType::Hive));
8127                let is_type_only = matches!(self.config.dialect, Some(DialectType::Redshift) | Some(DialectType::Spark) | Some(DialectType::Databricks));
8128                if is_type_only {
8129                    self.write_keyword("TYPE");
8130                    self.write_space();
8131                } else if !is_no_prefix {
8132                    self.write_keyword("SET DATA TYPE");
8133                    self.write_space();
8134                }
8135                self.generate_data_type(data_type)?;
8136                if let Some(ref collation) = collate {
8137                    self.write_space();
8138                    self.write_keyword("COLLATE");
8139                    self.write_space();
8140                    self.write(collation);
8141                }
8142                if let Some(ref using_expr) = using {
8143                    self.write_space();
8144                    self.write_keyword("USING");
8145                    self.write_space();
8146                    self.generate_expression(using_expr)?;
8147                }
8148            }
8149            AlterColumnAction::SetDefault(expr) => {
8150                self.write_keyword("SET DEFAULT");
8151                self.write_space();
8152                self.generate_expression(expr)?;
8153            }
8154            AlterColumnAction::DropDefault => {
8155                self.write_keyword("DROP DEFAULT");
8156            }
8157            AlterColumnAction::SetNotNull => {
8158                self.write_keyword("SET NOT NULL");
8159            }
8160            AlterColumnAction::DropNotNull => {
8161                self.write_keyword("DROP NOT NULL");
8162            }
8163            AlterColumnAction::Comment(comment) => {
8164                self.write_keyword("COMMENT");
8165                self.write_space();
8166                self.generate_string_literal(comment)?;
8167            }
8168            AlterColumnAction::SetVisible => {
8169                self.write_keyword("SET VISIBLE");
8170            }
8171            AlterColumnAction::SetInvisible => {
8172                self.write_keyword("SET INVISIBLE");
8173            }
8174        }
8175        Ok(())
8176    }
8177
8178    fn generate_create_index(&mut self, ci: &CreateIndex) -> Result<()> {
8179        self.write_keyword("CREATE");
8180
8181        if ci.unique {
8182            self.write_space();
8183            self.write_keyword("UNIQUE");
8184        }
8185
8186        // TSQL CLUSTERED/NONCLUSTERED modifier
8187        if let Some(ref clustered) = ci.clustered {
8188            self.write_space();
8189            self.write_keyword(clustered);
8190        }
8191
8192        self.write_space();
8193        self.write_keyword("INDEX");
8194
8195        // PostgreSQL CONCURRENTLY modifier
8196        if ci.concurrently {
8197            self.write_space();
8198            self.write_keyword("CONCURRENTLY");
8199        }
8200
8201        if ci.if_not_exists {
8202            self.write_space();
8203            self.write_keyword("IF NOT EXISTS");
8204        }
8205
8206        // Index name is optional in PostgreSQL when IF NOT EXISTS is specified
8207        if !ci.name.name.is_empty() {
8208            self.write_space();
8209            self.generate_identifier(&ci.name)?;
8210        }
8211        self.write_space();
8212        self.write_keyword("ON");
8213        self.write_space();
8214        self.generate_table(&ci.table)?;
8215
8216        // Column list (optional for COLUMNSTORE indexes)
8217        // Standard SQL convention: ON t(a) without space before paren
8218        if !ci.columns.is_empty() || ci.using.is_some() {
8219            let space_before_paren = false;
8220
8221            if let Some(ref using) = ci.using {
8222                self.write_space();
8223                self.write_keyword("USING");
8224                self.write_space();
8225                self.write(using);
8226                if space_before_paren {
8227                    self.write(" (");
8228                } else {
8229                    self.write("(");
8230                }
8231            } else {
8232                if space_before_paren {
8233                    self.write(" (");
8234                } else {
8235                    self.write("(");
8236                }
8237            }
8238            for (i, col) in ci.columns.iter().enumerate() {
8239                if i > 0 {
8240                    self.write(", ");
8241                }
8242                self.generate_identifier(&col.column)?;
8243                if let Some(ref opclass) = col.opclass {
8244                    self.write_space();
8245                    self.write(opclass);
8246                }
8247                if col.desc {
8248                    self.write_space();
8249                    self.write_keyword("DESC");
8250                } else if col.asc {
8251                    self.write_space();
8252                    self.write_keyword("ASC");
8253                }
8254                if let Some(nulls_first) = col.nulls_first {
8255                    self.write_space();
8256                    self.write_keyword("NULLS");
8257                    self.write_space();
8258                    self.write_keyword(if nulls_first { "FIRST" } else { "LAST" });
8259                }
8260            }
8261            self.write(")");
8262        }
8263
8264        // PostgreSQL INCLUDE (col1, col2) clause
8265        if !ci.include_columns.is_empty() {
8266            self.write_space();
8267            self.write_keyword("INCLUDE");
8268            self.write(" (");
8269            for (i, col) in ci.include_columns.iter().enumerate() {
8270                if i > 0 {
8271                    self.write(", ");
8272                }
8273                self.generate_identifier(col)?;
8274            }
8275            self.write(")");
8276        }
8277
8278        // TSQL: WITH (option=value, ...) clause
8279        if !ci.with_options.is_empty() {
8280            self.write_space();
8281            self.write_keyword("WITH");
8282            self.write(" (");
8283            for (i, (key, value)) in ci.with_options.iter().enumerate() {
8284                if i > 0 {
8285                    self.write(", ");
8286                }
8287                self.write(key);
8288                self.write("=");
8289                self.write(value);
8290            }
8291            self.write(")");
8292        }
8293
8294        // PostgreSQL WHERE clause for partial indexes
8295        if let Some(ref where_clause) = ci.where_clause {
8296            self.write_space();
8297            self.write_keyword("WHERE");
8298            self.write_space();
8299            self.generate_expression(where_clause)?;
8300        }
8301
8302        // TSQL: ON filegroup or partition scheme clause
8303        if let Some(ref on_fg) = ci.on_filegroup {
8304            self.write_space();
8305            self.write_keyword("ON");
8306            self.write_space();
8307            self.write(on_fg);
8308        }
8309
8310        Ok(())
8311    }
8312
8313    fn generate_drop_index(&mut self, di: &DropIndex) -> Result<()> {
8314        self.write_keyword("DROP INDEX");
8315
8316        if di.concurrently {
8317            self.write_space();
8318            self.write_keyword("CONCURRENTLY");
8319        }
8320
8321        if di.if_exists {
8322            self.write_space();
8323            self.write_keyword("IF EXISTS");
8324        }
8325
8326        self.write_space();
8327        self.generate_identifier(&di.name)?;
8328
8329        if let Some(ref table) = di.table {
8330            self.write_space();
8331            self.write_keyword("ON");
8332            self.write_space();
8333            self.generate_table(table)?;
8334        }
8335
8336        Ok(())
8337    }
8338
8339    fn generate_create_view(&mut self, cv: &CreateView) -> Result<()> {
8340        self.write_keyword("CREATE");
8341
8342        // MySQL: ALGORITHM=...
8343        if let Some(ref algorithm) = cv.algorithm {
8344            self.write_space();
8345            self.write_keyword("ALGORITHM");
8346            self.write("=");
8347            self.write_keyword(algorithm);
8348        }
8349
8350        // MySQL: DEFINER=...
8351        if let Some(ref definer) = cv.definer {
8352            self.write_space();
8353            self.write_keyword("DEFINER");
8354            self.write("=");
8355            self.write(definer);
8356        }
8357
8358        // MySQL: SQL SECURITY DEFINER/INVOKER (before VIEW keyword)
8359        if cv.security_sql_style {
8360            if let Some(ref security) = cv.security {
8361                self.write_space();
8362                self.write_keyword("SQL SECURITY");
8363                self.write_space();
8364                match security {
8365                    FunctionSecurity::Definer => self.write_keyword("DEFINER"),
8366                    FunctionSecurity::Invoker => self.write_keyword("INVOKER"),
8367                    FunctionSecurity::None => self.write_keyword("NONE"),
8368                }
8369            }
8370        }
8371
8372        if cv.or_replace {
8373            self.write_space();
8374            self.write_keyword("OR REPLACE");
8375        }
8376
8377        if cv.temporary {
8378            self.write_space();
8379            self.write_keyword("TEMPORARY");
8380        }
8381
8382        if cv.materialized {
8383            self.write_space();
8384            self.write_keyword("MATERIALIZED");
8385        }
8386
8387        // Snowflake: SECURE VIEW
8388        if cv.secure {
8389            self.write_space();
8390            self.write_keyword("SECURE");
8391        }
8392
8393        self.write_space();
8394        self.write_keyword("VIEW");
8395
8396        if cv.if_not_exists {
8397            self.write_space();
8398            self.write_keyword("IF NOT EXISTS");
8399        }
8400
8401        self.write_space();
8402        self.generate_table(&cv.name)?;
8403
8404        // ClickHouse: ON CLUSTER clause
8405        if let Some(ref on_cluster) = cv.on_cluster {
8406            self.write_space();
8407            self.generate_on_cluster(on_cluster)?;
8408        }
8409
8410        // ClickHouse: TO destination_table
8411        if let Some(ref to_table) = cv.to_table {
8412            self.write_space();
8413            self.write_keyword("TO");
8414            self.write_space();
8415            self.generate_table(to_table)?;
8416        }
8417
8418        // For regular VIEW: columns come before COPY GRANTS
8419        // For MATERIALIZED VIEW: COPY GRANTS comes before columns
8420        if !cv.materialized {
8421            // Regular VIEW: columns first
8422            if !cv.columns.is_empty() {
8423                self.write(" (");
8424                for (i, col) in cv.columns.iter().enumerate() {
8425                    if i > 0 {
8426                        self.write(", ");
8427                    }
8428                    self.generate_identifier(&col.name)?;
8429                    // BigQuery: OPTIONS (key=value, ...) on view column
8430                    if !col.options.is_empty() {
8431                        self.write_space();
8432                        self.generate_options_clause(&col.options)?;
8433                    }
8434                    if let Some(ref comment) = col.comment {
8435                        self.write_space();
8436                        self.write_keyword("COMMENT");
8437                        self.write_space();
8438                        self.generate_string_literal(comment)?;
8439                    }
8440                }
8441                self.write(")");
8442            }
8443
8444            // Presto/Trino/StarRocks: SECURITY DEFINER/INVOKER/NONE (after columns)
8445            if !cv.security_sql_style {
8446                if let Some(ref security) = cv.security {
8447                    self.write_space();
8448                    self.write_keyword("SECURITY");
8449                    self.write_space();
8450                    match security {
8451                        FunctionSecurity::Definer => self.write_keyword("DEFINER"),
8452                        FunctionSecurity::Invoker => self.write_keyword("INVOKER"),
8453                        FunctionSecurity::None => self.write_keyword("NONE"),
8454                    }
8455                }
8456            }
8457
8458            // Snowflake: COPY GRANTS
8459            if cv.copy_grants {
8460                self.write_space();
8461                self.write_keyword("COPY GRANTS");
8462            }
8463        } else {
8464            // MATERIALIZED VIEW: COPY GRANTS first
8465            if cv.copy_grants {
8466                self.write_space();
8467                self.write_keyword("COPY GRANTS");
8468            }
8469
8470            // Doris: If we have a schema (typed columns), generate that instead
8471            if let Some(ref schema) = cv.schema {
8472                self.write(" (");
8473                for (i, expr) in schema.expressions.iter().enumerate() {
8474                    if i > 0 {
8475                        self.write(", ");
8476                    }
8477                    self.generate_expression(expr)?;
8478                }
8479                self.write(")");
8480            } else if !cv.columns.is_empty() {
8481                // Then columns (simple column names without types)
8482                self.write(" (");
8483                for (i, col) in cv.columns.iter().enumerate() {
8484                    if i > 0 {
8485                        self.write(", ");
8486                    }
8487                    self.generate_identifier(&col.name)?;
8488                    // BigQuery: OPTIONS (key=value, ...) on view column
8489                    if !col.options.is_empty() {
8490                        self.write_space();
8491                        self.generate_options_clause(&col.options)?;
8492                    }
8493                    if let Some(ref comment) = col.comment {
8494                        self.write_space();
8495                        self.write_keyword("COMMENT");
8496                        self.write_space();
8497                        self.generate_string_literal(comment)?;
8498                    }
8499                }
8500                self.write(")");
8501            }
8502
8503            // Doris: KEY (columns) for materialized views
8504            if let Some(ref unique_key) = cv.unique_key {
8505                self.write_space();
8506                self.write_keyword("KEY");
8507                self.write(" (");
8508                for (i, expr) in unique_key.expressions.iter().enumerate() {
8509                    if i > 0 {
8510                        self.write(", ");
8511                    }
8512                    self.generate_expression(expr)?;
8513                }
8514                self.write(")");
8515            }
8516        }
8517
8518        // Snowflake: COMMENT = 'text'
8519        if let Some(ref comment) = cv.comment {
8520            self.write_space();
8521            self.write_keyword("COMMENT");
8522            self.write("=");
8523            self.generate_string_literal(comment)?;
8524        }
8525
8526        // Snowflake: TAG (name='value', ...)
8527        if !cv.tags.is_empty() {
8528            self.write_space();
8529            self.write_keyword("TAG");
8530            self.write(" (");
8531            for (i, (name, value)) in cv.tags.iter().enumerate() {
8532                if i > 0 {
8533                    self.write(", ");
8534                }
8535                self.write(name);
8536                self.write("='");
8537                self.write(value);
8538                self.write("'");
8539            }
8540            self.write(")");
8541        }
8542
8543        // BigQuery: OPTIONS (key=value, ...)
8544        if !cv.options.is_empty() {
8545            self.write_space();
8546            self.generate_options_clause(&cv.options)?;
8547        }
8548
8549        // Doris: BUILD IMMEDIATE/DEFERRED for materialized views
8550        if let Some(ref build) = cv.build {
8551            self.write_space();
8552            self.write_keyword("BUILD");
8553            self.write_space();
8554            self.write_keyword(build);
8555        }
8556
8557        // Doris: REFRESH clause for materialized views
8558        if let Some(ref refresh) = cv.refresh {
8559            self.write_space();
8560            self.generate_refresh_trigger_property(refresh)?;
8561        }
8562
8563        // Redshift: AUTO REFRESH YES|NO for materialized views
8564        if let Some(auto_refresh) = cv.auto_refresh {
8565            self.write_space();
8566            self.write_keyword("AUTO REFRESH");
8567            self.write_space();
8568            if auto_refresh {
8569                self.write_keyword("YES");
8570            } else {
8571                self.write_keyword("NO");
8572            }
8573        }
8574
8575        // ClickHouse: Table properties (ENGINE, ORDER BY, SAMPLE, SETTINGS, TTL, etc.)
8576        for prop in &cv.table_properties {
8577            self.write_space();
8578            self.generate_expression(prop)?;
8579        }
8580
8581        // Only output AS clause if there's a real query (not just NULL placeholder)
8582        if !matches!(&cv.query, Expression::Null(_)) {
8583            self.write_space();
8584            self.write_keyword("AS");
8585            self.write_space();
8586
8587            // Teradata: LOCKING clause (between AS and query)
8588            if let Some(ref mode) = cv.locking_mode {
8589                self.write_keyword("LOCKING");
8590                self.write_space();
8591                self.write_keyword(mode);
8592                if let Some(ref access) = cv.locking_access {
8593                    self.write_space();
8594                    self.write_keyword("FOR");
8595                    self.write_space();
8596                    self.write_keyword(access);
8597                }
8598                self.write_space();
8599            }
8600
8601            if cv.query_parenthesized {
8602                self.write("(");
8603            }
8604            self.generate_expression(&cv.query)?;
8605            if cv.query_parenthesized {
8606                self.write(")");
8607            }
8608        }
8609
8610        // Redshift: WITH NO SCHEMA BINDING (after query)
8611        if cv.no_schema_binding {
8612            self.write_space();
8613            self.write_keyword("WITH NO SCHEMA BINDING");
8614        }
8615
8616        Ok(())
8617    }
8618
8619    fn generate_drop_view(&mut self, dv: &DropView) -> Result<()> {
8620        self.write_keyword("DROP");
8621
8622        if dv.materialized {
8623            self.write_space();
8624            self.write_keyword("MATERIALIZED");
8625        }
8626
8627        self.write_space();
8628        self.write_keyword("VIEW");
8629
8630        if dv.if_exists {
8631            self.write_space();
8632            self.write_keyword("IF EXISTS");
8633        }
8634
8635        self.write_space();
8636        self.generate_table(&dv.name)?;
8637
8638        Ok(())
8639    }
8640
8641    fn generate_truncate(&mut self, tr: &Truncate) -> Result<()> {
8642        match tr.target {
8643            TruncateTarget::Database => self.write_keyword("TRUNCATE DATABASE"),
8644            TruncateTarget::Table => self.write_keyword("TRUNCATE TABLE"),
8645        }
8646        self.write_space();
8647        self.generate_table(&tr.table)?;
8648
8649        // ClickHouse: ON CLUSTER clause
8650        if let Some(ref on_cluster) = tr.on_cluster {
8651            self.write_space();
8652            self.generate_on_cluster(on_cluster)?;
8653        }
8654
8655        // Check if first table has a * (multi-table with star)
8656        if !tr.extra_tables.is_empty() {
8657            // Check if the first entry matches the main table (star case)
8658            let skip_first = if let Some(first) = tr.extra_tables.first() {
8659                first.table.name == tr.table.name && first.star
8660            } else {
8661                false
8662            };
8663
8664            // PostgreSQL normalizes away the * suffix (it's the default behavior)
8665            let strip_star = matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL) | Some(crate::dialects::DialectType::Redshift));
8666            if skip_first && !strip_star {
8667                self.write("*");
8668            }
8669
8670            // Generate additional tables
8671            for (i, entry) in tr.extra_tables.iter().enumerate() {
8672                if i == 0 && skip_first {
8673                    continue; // Already handled the star for first table
8674                }
8675                self.write(", ");
8676                self.generate_table(&entry.table)?;
8677                if entry.star && !strip_star {
8678                    self.write("*");
8679                }
8680            }
8681        }
8682
8683        // RESTART/CONTINUE IDENTITY
8684        if let Some(identity) = &tr.identity {
8685            self.write_space();
8686            match identity {
8687                TruncateIdentity::Restart => self.write_keyword("RESTART IDENTITY"),
8688                TruncateIdentity::Continue => self.write_keyword("CONTINUE IDENTITY"),
8689            }
8690        }
8691
8692        if tr.cascade {
8693            self.write_space();
8694            self.write_keyword("CASCADE");
8695        }
8696
8697        if tr.restrict {
8698            self.write_space();
8699            self.write_keyword("RESTRICT");
8700        }
8701
8702        // Output Hive PARTITION clause
8703        if let Some(ref partition) = tr.partition {
8704            self.write_space();
8705            self.generate_expression(partition)?;
8706        }
8707
8708        Ok(())
8709    }
8710
8711    fn generate_use(&mut self, u: &Use) -> Result<()> {
8712        // Teradata uses "DATABASE <name>" instead of "USE <name>"
8713        if matches!(self.config.dialect, Some(DialectType::Teradata)) {
8714            self.write_keyword("DATABASE");
8715            self.write_space();
8716            self.generate_identifier(&u.this)?;
8717            return Ok(());
8718        }
8719
8720        self.write_keyword("USE");
8721
8722        if let Some(kind) = &u.kind {
8723            self.write_space();
8724            match kind {
8725                UseKind::Database => self.write_keyword("DATABASE"),
8726                UseKind::Schema => self.write_keyword("SCHEMA"),
8727                UseKind::Role => self.write_keyword("ROLE"),
8728                UseKind::Warehouse => self.write_keyword("WAREHOUSE"),
8729                UseKind::Catalog => self.write_keyword("CATALOG"),
8730                UseKind::SecondaryRoles => self.write_keyword("SECONDARY ROLES"),
8731            }
8732        }
8733
8734        self.write_space();
8735        // For SECONDARY ROLES, write the value as-is (ALL, NONE, or role names)
8736        // without quoting, since these are keywords not identifiers
8737        if matches!(&u.kind, Some(UseKind::SecondaryRoles)) {
8738            self.write(&u.this.name);
8739        } else {
8740            self.generate_identifier(&u.this)?;
8741        }
8742        Ok(())
8743    }
8744
8745    fn generate_cache(&mut self, c: &Cache) -> Result<()> {
8746        self.write_keyword("CACHE");
8747        if c.lazy {
8748            self.write_space();
8749            self.write_keyword("LAZY");
8750        }
8751        self.write_space();
8752        self.write_keyword("TABLE");
8753        self.write_space();
8754        self.generate_identifier(&c.table)?;
8755
8756        // OPTIONS clause
8757        if !c.options.is_empty() {
8758            self.write_space();
8759            self.write_keyword("OPTIONS");
8760            self.write("(");
8761            for (i, (key, value)) in c.options.iter().enumerate() {
8762                if i > 0 {
8763                    self.write(", ");
8764                }
8765                self.generate_expression(key)?;
8766                self.write(" = ");
8767                self.generate_expression(value)?;
8768            }
8769            self.write(")");
8770        }
8771
8772        // AS query
8773        if let Some(query) = &c.query {
8774            self.write_space();
8775            self.write_keyword("AS");
8776            self.write_space();
8777            self.generate_expression(query)?;
8778        }
8779
8780        Ok(())
8781    }
8782
8783    fn generate_uncache(&mut self, u: &Uncache) -> Result<()> {
8784        self.write_keyword("UNCACHE TABLE");
8785        if u.if_exists {
8786            self.write_space();
8787            self.write_keyword("IF EXISTS");
8788        }
8789        self.write_space();
8790        self.generate_identifier(&u.table)?;
8791        Ok(())
8792    }
8793
8794    fn generate_load_data(&mut self, l: &LoadData) -> Result<()> {
8795        self.write_keyword("LOAD DATA");
8796        if l.local {
8797            self.write_space();
8798            self.write_keyword("LOCAL");
8799        }
8800        self.write_space();
8801        self.write_keyword("INPATH");
8802        self.write_space();
8803        self.write("'");
8804        self.write(&l.inpath);
8805        self.write("'");
8806
8807        if l.overwrite {
8808            self.write_space();
8809            self.write_keyword("OVERWRITE");
8810        }
8811
8812        self.write_space();
8813        self.write_keyword("INTO TABLE");
8814        self.write_space();
8815        self.generate_expression(&l.table)?;
8816
8817        // PARTITION clause
8818        if !l.partition.is_empty() {
8819            self.write_space();
8820            self.write_keyword("PARTITION");
8821            self.write("(");
8822            for (i, (col, val)) in l.partition.iter().enumerate() {
8823                if i > 0 {
8824                    self.write(", ");
8825                }
8826                self.generate_identifier(col)?;
8827                self.write(" = ");
8828                self.generate_expression(val)?;
8829            }
8830            self.write(")");
8831        }
8832
8833        // INPUTFORMAT clause
8834        if let Some(fmt) = &l.input_format {
8835            self.write_space();
8836            self.write_keyword("INPUTFORMAT");
8837            self.write_space();
8838            self.write("'");
8839            self.write(fmt);
8840            self.write("'");
8841        }
8842
8843        // SERDE clause
8844        if let Some(serde) = &l.serde {
8845            self.write_space();
8846            self.write_keyword("SERDE");
8847            self.write_space();
8848            self.write("'");
8849            self.write(serde);
8850            self.write("'");
8851        }
8852
8853        Ok(())
8854    }
8855
8856    fn generate_pragma(&mut self, p: &Pragma) -> Result<()> {
8857        self.write_keyword("PRAGMA");
8858        self.write_space();
8859
8860        // Schema prefix if present
8861        if let Some(schema) = &p.schema {
8862            self.generate_identifier(schema)?;
8863            self.write(".");
8864        }
8865
8866        // Pragma name
8867        self.generate_identifier(&p.name)?;
8868
8869        // Value assignment or function call
8870        if let Some(value) = &p.value {
8871            self.write(" = ");
8872            self.generate_expression(value)?;
8873        } else if !p.args.is_empty() {
8874            self.write("(");
8875            for (i, arg) in p.args.iter().enumerate() {
8876                if i > 0 {
8877                    self.write(", ");
8878                }
8879                self.generate_expression(arg)?;
8880            }
8881            self.write(")");
8882        }
8883
8884        Ok(())
8885    }
8886
8887    fn generate_grant(&mut self, g: &Grant) -> Result<()> {
8888        self.write_keyword("GRANT");
8889        self.write_space();
8890
8891        // Privileges (with optional column lists)
8892        for (i, privilege) in g.privileges.iter().enumerate() {
8893            if i > 0 {
8894                self.write(", ");
8895            }
8896            self.write_keyword(&privilege.name);
8897            // Output column list if present: SELECT(col1, col2)
8898            if !privilege.columns.is_empty() {
8899                self.write("(");
8900                for (j, col) in privilege.columns.iter().enumerate() {
8901                    if j > 0 {
8902                        self.write(", ");
8903                    }
8904                    self.write(col);
8905                }
8906                self.write(")");
8907            }
8908        }
8909
8910        self.write_space();
8911        self.write_keyword("ON");
8912        self.write_space();
8913
8914        // Object kind (TABLE, SCHEMA, etc.)
8915        if let Some(kind) = &g.kind {
8916            self.write_keyword(kind);
8917            self.write_space();
8918        }
8919
8920        // Securable - normalize function/procedure names to uppercase for PostgreSQL family
8921        {
8922            use crate::dialects::DialectType;
8923            let should_upper = matches!(
8924                self.config.dialect,
8925                Some(DialectType::PostgreSQL)
8926                    | Some(DialectType::CockroachDB)
8927                    | Some(DialectType::Materialize)
8928                    | Some(DialectType::RisingWave)
8929            ) && (g.kind.as_deref() == Some("FUNCTION") || g.kind.as_deref() == Some("PROCEDURE"));
8930            if should_upper {
8931                use crate::expressions::Identifier;
8932                let upper_id = Identifier {
8933                    name: g.securable.name.to_uppercase(),
8934                    quoted: g.securable.quoted,
8935                    ..g.securable.clone()
8936                };
8937                self.generate_identifier(&upper_id)?;
8938            } else {
8939                self.generate_identifier(&g.securable)?;
8940            }
8941        }
8942
8943        // Function parameter types (if present)
8944        if !g.function_params.is_empty() {
8945            self.write("(");
8946            for (i, param) in g.function_params.iter().enumerate() {
8947                if i > 0 {
8948                    self.write(", ");
8949                }
8950                self.write(param);
8951            }
8952            self.write(")");
8953        }
8954
8955        self.write_space();
8956        self.write_keyword("TO");
8957        self.write_space();
8958
8959        // Principals
8960        for (i, principal) in g.principals.iter().enumerate() {
8961            if i > 0 {
8962                self.write(", ");
8963            }
8964            if principal.is_role {
8965                self.write_keyword("ROLE");
8966                self.write_space();
8967            } else if principal.is_group {
8968                self.write_keyword("GROUP");
8969                self.write_space();
8970            }
8971            self.generate_identifier(&principal.name)?;
8972        }
8973
8974        // WITH GRANT OPTION
8975        if g.grant_option {
8976            self.write_space();
8977            self.write_keyword("WITH GRANT OPTION");
8978        }
8979
8980        // TSQL: AS principal
8981        if let Some(ref principal) = g.as_principal {
8982            self.write_space();
8983            self.write_keyword("AS");
8984            self.write_space();
8985            self.generate_identifier(principal)?;
8986        }
8987
8988        Ok(())
8989    }
8990
8991    fn generate_revoke(&mut self, r: &Revoke) -> Result<()> {
8992        self.write_keyword("REVOKE");
8993        self.write_space();
8994
8995        // GRANT OPTION FOR
8996        if r.grant_option {
8997            self.write_keyword("GRANT OPTION FOR");
8998            self.write_space();
8999        }
9000
9001        // Privileges (with optional column lists)
9002        for (i, privilege) in r.privileges.iter().enumerate() {
9003            if i > 0 {
9004                self.write(", ");
9005            }
9006            self.write_keyword(&privilege.name);
9007            // Output column list if present: SELECT(col1, col2)
9008            if !privilege.columns.is_empty() {
9009                self.write("(");
9010                for (j, col) in privilege.columns.iter().enumerate() {
9011                    if j > 0 {
9012                        self.write(", ");
9013                    }
9014                    self.write(col);
9015                }
9016                self.write(")");
9017            }
9018        }
9019
9020        self.write_space();
9021        self.write_keyword("ON");
9022        self.write_space();
9023
9024        // Object kind
9025        if let Some(kind) = &r.kind {
9026            self.write_keyword(kind);
9027            self.write_space();
9028        }
9029
9030        // Securable - normalize function/procedure names to uppercase for PostgreSQL family
9031        {
9032            use crate::dialects::DialectType;
9033            let should_upper = matches!(
9034                self.config.dialect,
9035                Some(DialectType::PostgreSQL)
9036                    | Some(DialectType::CockroachDB)
9037                    | Some(DialectType::Materialize)
9038                    | Some(DialectType::RisingWave)
9039            ) && (r.kind.as_deref() == Some("FUNCTION") || r.kind.as_deref() == Some("PROCEDURE"));
9040            if should_upper {
9041                use crate::expressions::Identifier;
9042                let upper_id = Identifier {
9043                    name: r.securable.name.to_uppercase(),
9044                    quoted: r.securable.quoted,
9045                    ..r.securable.clone()
9046                };
9047                self.generate_identifier(&upper_id)?;
9048            } else {
9049                self.generate_identifier(&r.securable)?;
9050            }
9051        }
9052
9053        // Function parameter types (if present)
9054        if !r.function_params.is_empty() {
9055            self.write("(");
9056            for (i, param) in r.function_params.iter().enumerate() {
9057                if i > 0 {
9058                    self.write(", ");
9059                }
9060                self.write(param);
9061            }
9062            self.write(")");
9063        }
9064
9065        self.write_space();
9066        self.write_keyword("FROM");
9067        self.write_space();
9068
9069        // Principals
9070        for (i, principal) in r.principals.iter().enumerate() {
9071            if i > 0 {
9072                self.write(", ");
9073            }
9074            if principal.is_role {
9075                self.write_keyword("ROLE");
9076                self.write_space();
9077            } else if principal.is_group {
9078                self.write_keyword("GROUP");
9079                self.write_space();
9080            }
9081            self.generate_identifier(&principal.name)?;
9082        }
9083
9084        // CASCADE or RESTRICT
9085        if r.cascade {
9086            self.write_space();
9087            self.write_keyword("CASCADE");
9088        } else if r.restrict {
9089            self.write_space();
9090            self.write_keyword("RESTRICT");
9091        }
9092
9093        Ok(())
9094    }
9095
9096    fn generate_comment(&mut self, c: &Comment) -> Result<()> {
9097        self.write_keyword("COMMENT");
9098
9099        // IF EXISTS
9100        if c.exists {
9101            self.write_space();
9102            self.write_keyword("IF EXISTS");
9103        }
9104
9105        self.write_space();
9106        self.write_keyword("ON");
9107
9108        // MATERIALIZED
9109        if c.materialized {
9110            self.write_space();
9111            self.write_keyword("MATERIALIZED");
9112        }
9113
9114        self.write_space();
9115        self.write_keyword(&c.kind);
9116        self.write_space();
9117
9118        // Object name
9119        self.generate_expression(&c.this)?;
9120
9121        self.write_space();
9122        self.write_keyword("IS");
9123        self.write_space();
9124
9125        // Comment expression
9126        self.generate_expression(&c.expression)?;
9127
9128        Ok(())
9129    }
9130
9131    fn generate_set_statement(&mut self, s: &SetStatement) -> Result<()> {
9132        self.write_keyword("SET");
9133
9134        for (i, item) in s.items.iter().enumerate() {
9135            if i > 0 {
9136                self.write(",");
9137            }
9138            self.write_space();
9139
9140            // Kind modifier (GLOBAL, LOCAL, SESSION, PERSIST, PERSIST_ONLY)
9141            if let Some(ref kind) = item.kind {
9142                self.write_keyword(kind);
9143                self.write_space();
9144            }
9145
9146            // Check for special SET forms by name
9147            let name_str = match &item.name {
9148                Expression::Identifier(id) => Some(id.name.as_str()),
9149                _ => None,
9150            };
9151
9152            let is_transaction = name_str == Some("TRANSACTION");
9153            let is_character_set = name_str == Some("CHARACTER SET");
9154            let is_names = name_str == Some("NAMES");
9155            let is_collate = name_str == Some("COLLATE");
9156            let has_variable_kind = item.kind.as_deref() == Some("VARIABLE");
9157            let name_has_variable_prefix = name_str.map_or(false, |n| n.starts_with("VARIABLE "));
9158            let is_variable = has_variable_kind || name_has_variable_prefix;
9159            let is_value_only = matches!(&item.value, Expression::Identifier(id) if id.name.is_empty());
9160
9161            if is_transaction {
9162                // Output: SET [GLOBAL|SESSION] TRANSACTION <characteristics>
9163                self.write_keyword("TRANSACTION");
9164                if let Expression::Identifier(id) = &item.value {
9165                    if !id.name.is_empty() {
9166                        self.write_space();
9167                        self.write(&id.name);
9168                    }
9169                }
9170            } else if is_character_set {
9171                // Output: SET CHARACTER SET <charset>
9172                self.write_keyword("CHARACTER SET");
9173                self.write_space();
9174                self.generate_set_value(&item.value)?;
9175            } else if is_names {
9176                // Output: SET NAMES <charset>
9177                self.write_keyword("NAMES");
9178                self.write_space();
9179                self.generate_set_value(&item.value)?;
9180            } else if is_collate {
9181                // Output: COLLATE <collation> (part of SET NAMES ... COLLATE ...)
9182                self.write_keyword("COLLATE");
9183                self.write_space();
9184                self.generate_set_value(&item.value)?;
9185            } else if is_variable {
9186                // Output: SET [VARIABLE] <name> = <value>
9187                // If kind=VARIABLE, the keyword was already written above.
9188                // If name has VARIABLE prefix, write VARIABLE keyword for DuckDB target only.
9189                if name_has_variable_prefix && !has_variable_kind {
9190                    if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
9191                        self.write_keyword("VARIABLE");
9192                        self.write_space();
9193                    }
9194                }
9195                // Extract actual variable name (strip VARIABLE prefix if present)
9196                if let Some(ns) = name_str {
9197                    let var_name = if name_has_variable_prefix {
9198                        &ns["VARIABLE ".len()..]
9199                    } else {
9200                        ns
9201                    };
9202                    self.write(var_name);
9203                } else {
9204                    self.generate_expression(&item.name)?;
9205                }
9206                self.write(" = ");
9207                self.generate_set_value(&item.value)?;
9208            } else if is_value_only {
9209                // SET <name> ON/OFF without = (TSQL: SET XACT_ABORT ON)
9210                self.generate_expression(&item.name)?;
9211            } else if item.no_equals && matches!(self.config.dialect, Some(DialectType::TSQL)) {
9212                // SET key value without = (TSQL style)
9213                self.generate_expression(&item.name)?;
9214                self.write_space();
9215                self.generate_set_value(&item.value)?;
9216            } else {
9217                // Standard: variable = value
9218                // SET item names should not be quoted (they are config parameter names, not column refs)
9219                match &item.name {
9220                    Expression::Identifier(id) => {
9221                        self.write(&id.name);
9222                    }
9223                    _ => {
9224                        self.generate_expression(&item.name)?;
9225                    }
9226                }
9227                self.write(" = ");
9228                self.generate_set_value(&item.value)?;
9229            }
9230        }
9231
9232        Ok(())
9233    }
9234
9235    /// Generate a SET statement value, writing keyword values (DEFAULT, ON, OFF)
9236    /// directly to avoid reserved keyword quoting.
9237    fn generate_set_value(&mut self, value: &Expression) -> Result<()> {
9238        if let Expression::Identifier(id) = value {
9239            match id.name.as_str() {
9240                "DEFAULT" | "ON" | "OFF" => {
9241                    self.write_keyword(&id.name);
9242                    return Ok(());
9243                }
9244                _ => {}
9245            }
9246        }
9247        self.generate_expression(value)
9248    }
9249
9250    // ==================== Phase 4: Additional DDL Generation ====================
9251
9252    fn generate_alter_view(&mut self, av: &AlterView) -> Result<()> {
9253        self.write_keyword("ALTER");
9254        // MySQL modifiers before VIEW
9255        if let Some(ref algorithm) = av.algorithm {
9256            self.write_space();
9257            self.write_keyword("ALGORITHM");
9258            self.write(" = ");
9259            self.write_keyword(algorithm);
9260        }
9261        if let Some(ref definer) = av.definer {
9262            self.write_space();
9263            self.write_keyword("DEFINER");
9264            self.write(" = ");
9265            self.write(definer);
9266        }
9267        if let Some(ref sql_security) = av.sql_security {
9268            self.write_space();
9269            self.write_keyword("SQL SECURITY");
9270            self.write(" = ");
9271            self.write_keyword(sql_security);
9272        }
9273        self.write_space();
9274        self.write_keyword("VIEW");
9275        self.write_space();
9276        self.generate_table(&av.name)?;
9277
9278        // Hive: Column aliases with optional COMMENT
9279        if !av.columns.is_empty() {
9280            self.write(" (");
9281            for (i, col) in av.columns.iter().enumerate() {
9282                if i > 0 {
9283                    self.write(", ");
9284                }
9285                self.generate_identifier(&col.name)?;
9286                if let Some(ref comment) = col.comment {
9287                    self.write_space();
9288                    self.write_keyword("COMMENT");
9289                    self.write(" ");
9290                    self.generate_string_literal(comment)?;
9291                }
9292            }
9293            self.write(")");
9294        }
9295
9296        // TSQL: WITH option before actions
9297        if let Some(ref opt) = av.with_option {
9298            self.write_space();
9299            self.write_keyword("WITH");
9300            self.write_space();
9301            self.write_keyword(opt);
9302        }
9303
9304        for action in &av.actions {
9305            self.write_space();
9306            match action {
9307                AlterViewAction::Rename(new_name) => {
9308                    self.write_keyword("RENAME TO");
9309                    self.write_space();
9310                    self.generate_table(new_name)?;
9311                }
9312                AlterViewAction::OwnerTo(owner) => {
9313                    self.write_keyword("OWNER TO");
9314                    self.write_space();
9315                    self.generate_identifier(owner)?;
9316                }
9317                AlterViewAction::SetSchema(schema) => {
9318                    self.write_keyword("SET SCHEMA");
9319                    self.write_space();
9320                    self.generate_identifier(schema)?;
9321                }
9322                AlterViewAction::SetAuthorization(auth) => {
9323                    self.write_keyword("SET AUTHORIZATION");
9324                    self.write_space();
9325                    self.write(auth);
9326                }
9327                AlterViewAction::AlterColumn { name, action } => {
9328                    self.write_keyword("ALTER COLUMN");
9329                    self.write_space();
9330                    self.generate_identifier(name)?;
9331                    self.write_space();
9332                    self.generate_alter_column_action(action)?;
9333                }
9334                AlterViewAction::AsSelect(query) => {
9335                    self.write_keyword("AS");
9336                    self.write_space();
9337                    self.generate_expression(query)?;
9338                }
9339                AlterViewAction::SetTblproperties(props) => {
9340                    self.write_keyword("SET TBLPROPERTIES");
9341                    self.write(" (");
9342                    for (i, (key, value)) in props.iter().enumerate() {
9343                        if i > 0 {
9344                            self.write(", ");
9345                        }
9346                        self.generate_string_literal(key)?;
9347                        self.write("=");
9348                        self.generate_string_literal(value)?;
9349                    }
9350                    self.write(")");
9351                }
9352                AlterViewAction::UnsetTblproperties(keys) => {
9353                    self.write_keyword("UNSET TBLPROPERTIES");
9354                    self.write(" (");
9355                    for (i, key) in keys.iter().enumerate() {
9356                        if i > 0 {
9357                            self.write(", ");
9358                        }
9359                        self.generate_string_literal(key)?;
9360                    }
9361                    self.write(")");
9362                }
9363            }
9364        }
9365
9366        Ok(())
9367    }
9368
9369    fn generate_alter_index(&mut self, ai: &AlterIndex) -> Result<()> {
9370        self.write_keyword("ALTER INDEX");
9371        self.write_space();
9372        self.generate_identifier(&ai.name)?;
9373
9374        if let Some(table) = &ai.table {
9375            self.write_space();
9376            self.write_keyword("ON");
9377            self.write_space();
9378            self.generate_table(table)?;
9379        }
9380
9381        for action in &ai.actions {
9382            self.write_space();
9383            match action {
9384                AlterIndexAction::Rename(new_name) => {
9385                    self.write_keyword("RENAME TO");
9386                    self.write_space();
9387                    self.generate_identifier(new_name)?;
9388                }
9389                AlterIndexAction::SetTablespace(tablespace) => {
9390                    self.write_keyword("SET TABLESPACE");
9391                    self.write_space();
9392                    self.generate_identifier(tablespace)?;
9393                }
9394                AlterIndexAction::Visible(visible) => {
9395                    if *visible {
9396                        self.write_keyword("VISIBLE");
9397                    } else {
9398                        self.write_keyword("INVISIBLE");
9399                    }
9400                }
9401            }
9402        }
9403
9404        Ok(())
9405    }
9406
9407    fn generate_create_schema(&mut self, cs: &CreateSchema) -> Result<()> {
9408        // Output leading comments
9409        for comment in &cs.leading_comments {
9410            self.write(comment);
9411            self.write_space();
9412        }
9413
9414        // Athena: CREATE SCHEMA uses Hive engine (backticks)
9415        let saved_athena_hive_context = self.athena_hive_context;
9416        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
9417            self.athena_hive_context = true;
9418        }
9419
9420        self.write_keyword("CREATE SCHEMA");
9421
9422        if cs.if_not_exists {
9423            self.write_space();
9424            self.write_keyword("IF NOT EXISTS");
9425        }
9426
9427        self.write_space();
9428        self.generate_identifier(&cs.name)?;
9429
9430        if let Some(ref clone_src) = cs.clone_from {
9431            self.write_keyword(" CLONE ");
9432            self.generate_identifier(clone_src)?;
9433        }
9434
9435        if let Some(ref at_clause) = cs.at_clause {
9436            self.write_space();
9437            self.generate_expression(at_clause)?;
9438        }
9439
9440        if let Some(auth) = &cs.authorization {
9441            self.write_space();
9442            self.write_keyword("AUTHORIZATION");
9443            self.write_space();
9444            self.generate_identifier(auth)?;
9445        }
9446
9447        // Generate schema properties (e.g., DEFAULT COLLATE or WITH (props))
9448        // Separate WITH properties from other properties
9449        let with_properties: Vec<_> = cs.properties.iter()
9450            .filter(|p| matches!(p, Expression::Property(_)))
9451            .collect();
9452        let other_properties: Vec<_> = cs.properties.iter()
9453            .filter(|p| !matches!(p, Expression::Property(_)))
9454            .collect();
9455
9456        // Generate WITH (props) if we have Property expressions
9457        if !with_properties.is_empty() {
9458            self.write_space();
9459            self.write_keyword("WITH");
9460            self.write(" (");
9461            for (i, prop) in with_properties.iter().enumerate() {
9462                if i > 0 {
9463                    self.write(", ");
9464                }
9465                self.generate_expression(prop)?;
9466            }
9467            self.write(")");
9468        }
9469
9470        // Generate other properties (like DEFAULT COLLATE)
9471        for prop in other_properties {
9472            self.write_space();
9473            self.generate_expression(prop)?;
9474        }
9475
9476        // Restore Athena Hive context
9477        self.athena_hive_context = saved_athena_hive_context;
9478
9479        Ok(())
9480    }
9481
9482    fn generate_drop_schema(&mut self, ds: &DropSchema) -> Result<()> {
9483        self.write_keyword("DROP SCHEMA");
9484
9485        if ds.if_exists {
9486            self.write_space();
9487            self.write_keyword("IF EXISTS");
9488        }
9489
9490        self.write_space();
9491        self.generate_identifier(&ds.name)?;
9492
9493        if ds.cascade {
9494            self.write_space();
9495            self.write_keyword("CASCADE");
9496        }
9497
9498        Ok(())
9499    }
9500
9501    fn generate_drop_namespace(&mut self, dn: &DropNamespace) -> Result<()> {
9502        self.write_keyword("DROP NAMESPACE");
9503
9504        if dn.if_exists {
9505            self.write_space();
9506            self.write_keyword("IF EXISTS");
9507        }
9508
9509        self.write_space();
9510        self.generate_identifier(&dn.name)?;
9511
9512        if dn.cascade {
9513            self.write_space();
9514            self.write_keyword("CASCADE");
9515        }
9516
9517        Ok(())
9518    }
9519
9520    fn generate_create_database(&mut self, cd: &CreateDatabase) -> Result<()> {
9521        self.write_keyword("CREATE DATABASE");
9522
9523        if cd.if_not_exists {
9524            self.write_space();
9525            self.write_keyword("IF NOT EXISTS");
9526        }
9527
9528        self.write_space();
9529        self.generate_identifier(&cd.name)?;
9530
9531        if let Some(ref clone_src) = cd.clone_from {
9532            self.write_keyword(" CLONE ");
9533            self.generate_identifier(clone_src)?;
9534        }
9535
9536        // AT/BEFORE clause for time travel (Snowflake)
9537        if let Some(ref at_clause) = cd.at_clause {
9538            self.write_space();
9539            self.generate_expression(at_clause)?;
9540        }
9541
9542        for option in &cd.options {
9543            self.write_space();
9544            match option {
9545                DatabaseOption::CharacterSet(charset) => {
9546                    self.write_keyword("CHARACTER SET");
9547                    self.write(" = ");
9548                    self.write(&format!("'{}'", charset));
9549                }
9550                DatabaseOption::Collate(collate) => {
9551                    self.write_keyword("COLLATE");
9552                    self.write(" = ");
9553                    self.write(&format!("'{}'", collate));
9554                }
9555                DatabaseOption::Owner(owner) => {
9556                    self.write_keyword("OWNER");
9557                    self.write(" = ");
9558                    self.generate_identifier(owner)?;
9559                }
9560                DatabaseOption::Template(template) => {
9561                    self.write_keyword("TEMPLATE");
9562                    self.write(" = ");
9563                    self.generate_identifier(template)?;
9564                }
9565                DatabaseOption::Encoding(encoding) => {
9566                    self.write_keyword("ENCODING");
9567                    self.write(" = ");
9568                    self.write(&format!("'{}'", encoding));
9569                }
9570                DatabaseOption::Location(location) => {
9571                    self.write_keyword("LOCATION");
9572                    self.write(" = ");
9573                    self.write(&format!("'{}'", location));
9574                }
9575            }
9576        }
9577
9578        Ok(())
9579    }
9580
9581    fn generate_drop_database(&mut self, dd: &DropDatabase) -> Result<()> {
9582        self.write_keyword("DROP DATABASE");
9583
9584        if dd.if_exists {
9585            self.write_space();
9586            self.write_keyword("IF EXISTS");
9587        }
9588
9589        self.write_space();
9590        self.generate_identifier(&dd.name)?;
9591
9592        Ok(())
9593    }
9594
9595    fn generate_create_function(&mut self, cf: &CreateFunction) -> Result<()> {
9596        self.write_keyword("CREATE");
9597
9598        if cf.or_replace {
9599            self.write_space();
9600            self.write_keyword("OR REPLACE");
9601        }
9602
9603        if cf.temporary {
9604            self.write_space();
9605            self.write_keyword("TEMPORARY");
9606        }
9607
9608        self.write_space();
9609        if cf.is_table_function {
9610            self.write_keyword("TABLE FUNCTION");
9611        } else {
9612            self.write_keyword("FUNCTION");
9613        }
9614
9615        if cf.if_not_exists {
9616            self.write_space();
9617            self.write_keyword("IF NOT EXISTS");
9618        }
9619
9620        self.write_space();
9621        self.generate_table(&cf.name)?;
9622        if cf.has_parens {
9623            let func_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)) && !cf.parameters.is_empty();
9624            if func_multiline {
9625                self.write("(\n");
9626                self.indent_level += 2;
9627                self.write_indent();
9628                self.generate_function_parameters(&cf.parameters)?;
9629                self.write("\n");
9630                self.indent_level -= 2;
9631                self.write(")");
9632            } else {
9633                self.write("(");
9634                self.generate_function_parameters(&cf.parameters)?;
9635                self.write(")");
9636            }
9637        }
9638
9639        // Output RETURNS clause (always comes first after parameters)
9640        // BigQuery and TSQL use multiline formatting for CREATE FUNCTION structure
9641        let use_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery) | Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric));
9642
9643        if cf.language_first {
9644            // LANGUAGE first, then SQL data access, then RETURNS
9645            if let Some(lang) = &cf.language {
9646                if use_multiline {
9647                    self.write_newline();
9648                } else {
9649                    self.write_space();
9650                }
9651                self.write_keyword("LANGUAGE");
9652                self.write_space();
9653                self.write(lang);
9654            }
9655
9656            // SQL data access comes after LANGUAGE in this case
9657            if let Some(sql_data) = &cf.sql_data_access {
9658                self.write_space();
9659                match sql_data {
9660                    SqlDataAccess::NoSql => self.write_keyword("NO SQL"),
9661                    SqlDataAccess::ContainsSql => self.write_keyword("CONTAINS SQL"),
9662                    SqlDataAccess::ReadsSqlData => self.write_keyword("READS SQL DATA"),
9663                    SqlDataAccess::ModifiesSqlData => self.write_keyword("MODIFIES SQL DATA"),
9664                }
9665            }
9666
9667            if let Some(ref rtb) = cf.returns_table_body {
9668                if use_multiline {
9669                    self.write_newline();
9670                } else {
9671                    self.write_space();
9672                }
9673                self.write_keyword("RETURNS");
9674                self.write_space();
9675                self.write(rtb);
9676            } else if let Some(return_type) = &cf.return_type {
9677                if use_multiline {
9678                    self.write_newline();
9679                } else {
9680                    self.write_space();
9681                }
9682                self.write_keyword("RETURNS");
9683                self.write_space();
9684                self.generate_data_type(return_type)?;
9685            }
9686        } else {
9687            // RETURNS first (default)
9688            // DuckDB macros: skip RETURNS output (empty marker in returns_table_body means TABLE return)
9689            let is_duckdb = matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB));
9690            if let Some(ref rtb) = cf.returns_table_body {
9691                if !(is_duckdb && rtb.is_empty()) {
9692                    if use_multiline {
9693                        self.write_newline();
9694                    } else {
9695                        self.write_space();
9696                    }
9697                    self.write_keyword("RETURNS");
9698                    self.write_space();
9699                    self.write(rtb);
9700                }
9701            } else if let Some(return_type) = &cf.return_type {
9702                if use_multiline {
9703                    self.write_newline();
9704                } else {
9705                    self.write_space();
9706                }
9707                self.write_keyword("RETURNS");
9708                self.write_space();
9709                self.generate_data_type(return_type)?;
9710            }
9711        }
9712
9713        // If we have property_order, use it to output properties in original order
9714        if !cf.property_order.is_empty() {
9715            // For BigQuery, OPTIONS must come before AS - reorder if needed
9716            let is_bigquery = matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery));
9717            let property_order = if is_bigquery {
9718                // Move Options before As if both are present
9719                let mut reordered = Vec::new();
9720                let mut has_as = false;
9721                let mut has_options = false;
9722                for prop in &cf.property_order {
9723                    match prop {
9724                        FunctionPropertyKind::As => has_as = true,
9725                        FunctionPropertyKind::Options => has_options = true,
9726                        _ => {}
9727                    }
9728                }
9729                if has_as && has_options {
9730                    // Output all props except As and Options, then Options, then As
9731                    for prop in &cf.property_order {
9732                        if *prop != FunctionPropertyKind::As && *prop != FunctionPropertyKind::Options {
9733                            reordered.push(*prop);
9734                        }
9735                    }
9736                    reordered.push(FunctionPropertyKind::Options);
9737                    reordered.push(FunctionPropertyKind::As);
9738                    reordered
9739                } else {
9740                    cf.property_order.clone()
9741                }
9742            } else {
9743                cf.property_order.clone()
9744            };
9745
9746            for prop in &property_order {
9747                match prop {
9748                    FunctionPropertyKind::Set => {
9749                        self.generate_function_set_options(cf)?;
9750                    }
9751                    FunctionPropertyKind::As => {
9752                        self.generate_function_body(cf)?;
9753                    }
9754                    FunctionPropertyKind::Language => {
9755                        if !cf.language_first {
9756                            // Only output here if not already output above
9757                            if let Some(lang) = &cf.language {
9758                                // Only BigQuery uses multiline formatting
9759                                let use_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery));
9760                                if use_multiline {
9761                                    self.write_newline();
9762                                } else {
9763                                    self.write_space();
9764                                }
9765                                self.write_keyword("LANGUAGE");
9766                                self.write_space();
9767                                self.write(lang);
9768                            }
9769                        }
9770                    }
9771                    FunctionPropertyKind::Determinism => {
9772                        self.generate_function_determinism(cf)?;
9773                    }
9774                    FunctionPropertyKind::NullInput => {
9775                        self.generate_function_null_input(cf)?;
9776                    }
9777                    FunctionPropertyKind::Security => {
9778                        self.generate_function_security(cf)?;
9779                    }
9780                    FunctionPropertyKind::SqlDataAccess => {
9781                        if !cf.language_first {
9782                            // Only output here if not already output above
9783                            self.generate_function_sql_data_access(cf)?;
9784                        }
9785                    }
9786                    FunctionPropertyKind::Options => {
9787                        if !cf.options.is_empty() {
9788                            self.write_space();
9789                            self.generate_options_clause(&cf.options)?;
9790                        }
9791                    }
9792                    FunctionPropertyKind::Environment => {
9793                        if !cf.environment.is_empty() {
9794                            self.write_space();
9795                            self.generate_environment_clause(&cf.environment)?;
9796                        }
9797                    }
9798                }
9799            }
9800
9801            // Output OPTIONS if not tracked in property_order (legacy)
9802            if !cf.options.is_empty() && !cf.property_order.contains(&FunctionPropertyKind::Options) {
9803                self.write_space();
9804                self.generate_options_clause(&cf.options)?;
9805            }
9806
9807            // Output ENVIRONMENT if not tracked in property_order (legacy)
9808            if !cf.environment.is_empty() && !cf.property_order.contains(&FunctionPropertyKind::Environment) {
9809                self.write_space();
9810                self.generate_environment_clause(&cf.environment)?;
9811            }
9812        } else {
9813            // Legacy behavior when property_order is empty
9814            // BigQuery: DETERMINISTIC/NOT DETERMINISTIC comes before LANGUAGE
9815            if matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) {
9816                self.generate_function_determinism(cf)?;
9817            }
9818
9819            // Only BigQuery uses multiline formatting for CREATE FUNCTION structure
9820            let use_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery));
9821
9822            if !cf.language_first {
9823                if let Some(lang) = &cf.language {
9824                    if use_multiline {
9825                        self.write_newline();
9826                    } else {
9827                        self.write_space();
9828                    }
9829                    self.write_keyword("LANGUAGE");
9830                    self.write_space();
9831                    self.write(lang);
9832                }
9833
9834                // SQL data access characteristic comes after LANGUAGE
9835                self.generate_function_sql_data_access(cf)?;
9836            }
9837
9838            // For non-BigQuery dialects, output DETERMINISTIC/IMMUTABLE/VOLATILE here
9839            if !matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) {
9840                self.generate_function_determinism(cf)?;
9841            }
9842
9843            self.generate_function_null_input(cf)?;
9844            self.generate_function_security(cf)?;
9845            self.generate_function_set_options(cf)?;
9846
9847            // BigQuery: OPTIONS (key=value, ...) - comes before AS
9848            if !cf.options.is_empty() {
9849                self.write_space();
9850                self.generate_options_clause(&cf.options)?;
9851            }
9852
9853            // Databricks: ENVIRONMENT (dependencies = '...', ...) - comes before AS
9854            if !cf.environment.is_empty() {
9855                self.write_space();
9856                self.generate_environment_clause(&cf.environment)?;
9857            }
9858
9859            self.generate_function_body(cf)?;
9860        }
9861
9862        Ok(())
9863    }
9864
9865    /// Generate SET options for CREATE FUNCTION
9866    fn generate_function_set_options(&mut self, cf: &CreateFunction) -> Result<()> {
9867        for opt in &cf.set_options {
9868            self.write_space();
9869            self.write_keyword("SET");
9870            self.write_space();
9871            self.write(&opt.name);
9872            match &opt.value {
9873                FunctionSetValue::Value { value, use_to } => {
9874                    // PostgreSQL normalizes TO to = (matching Python sqlglot behavior)
9875                    if *use_to && !matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL)) {
9876                        self.write(" TO ");
9877                    } else {
9878                        self.write(" = ");
9879                    }
9880                    self.write(value);
9881                }
9882                FunctionSetValue::FromCurrent => {
9883                    self.write_space();
9884                    self.write_keyword("FROM CURRENT");
9885                }
9886            }
9887        }
9888        Ok(())
9889    }
9890
9891    /// Generate function body (AS clause)
9892    fn generate_function_body(&mut self, cf: &CreateFunction) -> Result<()> {
9893        if let Some(body) = &cf.body {
9894            // AS stays on same line as previous content (e.g., LANGUAGE js AS)
9895            self.write_space();
9896            // Only BigQuery uses multiline formatting for CREATE FUNCTION body
9897            let use_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery));
9898            match body {
9899                FunctionBody::Block(block) => {
9900                    self.write_keyword("AS");
9901                    if matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL)) {
9902                        self.write(" BEGIN ");
9903                        self.write(block);
9904                        self.write(" END");
9905                    } else if matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL)) {
9906                        self.write(" $$");
9907                        self.write(block);
9908                        self.write("$$");
9909                    } else {
9910                        // Escape content for single-quoted output
9911                        let escaped = self.escape_block_for_single_quote(block);
9912                        // In BigQuery pretty mode, body content goes on new line
9913                        if use_multiline {
9914                            self.write_newline();
9915                        } else {
9916                            self.write(" ");
9917                        }
9918                        self.write("'");
9919                        self.write(&escaped);
9920                        self.write("'");
9921                    }
9922                }
9923                FunctionBody::StringLiteral(s) => {
9924                    self.write_keyword("AS");
9925                    // In BigQuery pretty mode, body content goes on new line
9926                    if use_multiline {
9927                        self.write_newline();
9928                    } else {
9929                        self.write(" ");
9930                    }
9931                    self.write("'");
9932                    self.write(s);
9933                    self.write("'");
9934                }
9935                FunctionBody::Expression(expr) => {
9936                    self.write_keyword("AS");
9937                    self.write_space();
9938                    self.generate_expression(expr)?;
9939                }
9940                FunctionBody::External(name) => {
9941                    self.write_keyword("EXTERNAL NAME");
9942                    self.write(" '");
9943                    self.write(name);
9944                    self.write("'");
9945                }
9946                FunctionBody::Return(expr) => {
9947                    if matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB)) {
9948                        // DuckDB macro syntax: AS [TABLE] expression (no RETURN keyword)
9949                        self.write_keyword("AS");
9950                        self.write_space();
9951                        // Empty returns_table_body signals TABLE return
9952                        if cf.returns_table_body.is_some() {
9953                            self.write_keyword("TABLE");
9954                            self.write_space();
9955                        }
9956                        self.generate_expression(expr)?;
9957                    } else {
9958                        if self.config.create_function_return_as {
9959                            self.write_keyword("AS");
9960                            // TSQL pretty: newline between AS and RETURN
9961                            if self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)) {
9962                                self.write_newline();
9963                            } else {
9964                                self.write_space();
9965                            }
9966                        }
9967                        self.write_keyword("RETURN");
9968                        self.write_space();
9969                        self.generate_expression(expr)?;
9970                    }
9971                }
9972                FunctionBody::Statements(stmts) => {
9973                    self.write_keyword("AS");
9974                    self.write(" BEGIN ");
9975                    for (i, stmt) in stmts.iter().enumerate() {
9976                        if i > 0 {
9977                            self.write(" ");
9978                        }
9979                        self.generate_expression(stmt)?;
9980                    }
9981                    self.write(" END");
9982                }
9983                FunctionBody::DollarQuoted { content, tag } => {
9984                    self.write_keyword("AS");
9985                    self.write(" ");
9986                    // Dialects that support dollar-quoted strings: PostgreSQL, Databricks, Redshift, DuckDB
9987                    let supports_dollar_quoting = matches!(
9988                        self.config.dialect,
9989                        Some(crate::dialects::DialectType::PostgreSQL)
9990                            | Some(crate::dialects::DialectType::Databricks)
9991                            | Some(crate::dialects::DialectType::Redshift)
9992                            | Some(crate::dialects::DialectType::DuckDB)
9993                    );
9994                    if supports_dollar_quoting {
9995                        // Output in dollar-quoted format
9996                        self.write("$");
9997                        if let Some(t) = tag {
9998                            self.write(t);
9999                        }
10000                        self.write("$");
10001                        self.write(content);
10002                        self.write("$");
10003                        if let Some(t) = tag {
10004                            self.write(t);
10005                        }
10006                        self.write("$");
10007                    } else {
10008                        // Convert to single-quoted string for other dialects
10009                        let escaped = self.escape_block_for_single_quote(content);
10010                        self.write("'");
10011                        self.write(&escaped);
10012                        self.write("'");
10013                    }
10014                }
10015            }
10016        }
10017        Ok(())
10018    }
10019
10020    /// Generate determinism clause (IMMUTABLE/VOLATILE/DETERMINISTIC)
10021    fn generate_function_determinism(&mut self, cf: &CreateFunction) -> Result<()> {
10022        if let Some(det) = cf.deterministic {
10023            self.write_space();
10024            if matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) {
10025                // BigQuery uses DETERMINISTIC/NOT DETERMINISTIC
10026                if det {
10027                    self.write_keyword("DETERMINISTIC");
10028                } else {
10029                    self.write_keyword("NOT DETERMINISTIC");
10030                }
10031            } else {
10032                // PostgreSQL and others use IMMUTABLE/VOLATILE
10033                if det {
10034                    self.write_keyword("IMMUTABLE");
10035                } else {
10036                    self.write_keyword("VOLATILE");
10037                }
10038            }
10039        }
10040        Ok(())
10041    }
10042
10043    /// Generate null input handling clause
10044    fn generate_function_null_input(&mut self, cf: &CreateFunction) -> Result<()> {
10045        if let Some(returns_null) = cf.returns_null_on_null_input {
10046            self.write_space();
10047            if returns_null {
10048                if cf.strict {
10049                    self.write_keyword("STRICT");
10050                } else {
10051                    self.write_keyword("RETURNS NULL ON NULL INPUT");
10052                }
10053            } else {
10054                self.write_keyword("CALLED ON NULL INPUT");
10055            }
10056        }
10057        Ok(())
10058    }
10059
10060    /// Generate security clause
10061    fn generate_function_security(&mut self, cf: &CreateFunction) -> Result<()> {
10062        if let Some(security) = &cf.security {
10063            self.write_space();
10064            self.write_keyword("SECURITY");
10065            self.write_space();
10066            match security {
10067                FunctionSecurity::Definer => self.write_keyword("DEFINER"),
10068                FunctionSecurity::Invoker => self.write_keyword("INVOKER"),
10069                FunctionSecurity::None => self.write_keyword("NONE"),
10070            }
10071        }
10072        Ok(())
10073    }
10074
10075    /// Generate SQL data access clause
10076    fn generate_function_sql_data_access(&mut self, cf: &CreateFunction) -> Result<()> {
10077        if let Some(sql_data) = &cf.sql_data_access {
10078            self.write_space();
10079            match sql_data {
10080                SqlDataAccess::NoSql => self.write_keyword("NO SQL"),
10081                SqlDataAccess::ContainsSql => self.write_keyword("CONTAINS SQL"),
10082                SqlDataAccess::ReadsSqlData => self.write_keyword("READS SQL DATA"),
10083                SqlDataAccess::ModifiesSqlData => self.write_keyword("MODIFIES SQL DATA"),
10084            }
10085        }
10086        Ok(())
10087    }
10088
10089    fn generate_function_parameters(&mut self, params: &[FunctionParameter]) -> Result<()> {
10090        for (i, param) in params.iter().enumerate() {
10091            if i > 0 {
10092                self.write(", ");
10093            }
10094
10095            if let Some(mode) = &param.mode {
10096                match mode {
10097                    ParameterMode::In => self.write_keyword("IN"),
10098                    ParameterMode::Out => self.write_keyword("OUT"),
10099                    ParameterMode::InOut => self.write_keyword("INOUT"),
10100                }
10101                self.write_space();
10102            }
10103
10104            if let Some(name) = &param.name {
10105                self.generate_identifier(name)?;
10106                // Skip space and type for empty Custom types (e.g., DuckDB macros)
10107                let skip_type = matches!(&param.data_type, DataType::Custom { name } if name.is_empty());
10108                if !skip_type {
10109                    self.write_space();
10110                    self.generate_data_type(&param.data_type)?;
10111                }
10112            } else {
10113                self.generate_data_type(&param.data_type)?;
10114            }
10115
10116            if let Some(default) = &param.default {
10117                if self.config.parameter_default_equals {
10118                    self.write(" = ");
10119                } else {
10120                    self.write(" DEFAULT ");
10121                }
10122                self.generate_expression(default)?;
10123            }
10124        }
10125
10126        Ok(())
10127    }
10128
10129    fn generate_drop_function(&mut self, df: &DropFunction) -> Result<()> {
10130        self.write_keyword("DROP FUNCTION");
10131
10132        if df.if_exists {
10133            self.write_space();
10134            self.write_keyword("IF EXISTS");
10135        }
10136
10137        self.write_space();
10138        self.generate_table(&df.name)?;
10139
10140        if let Some(params) = &df.parameters {
10141            self.write(" (");
10142            for (i, dt) in params.iter().enumerate() {
10143                if i > 0 {
10144                    self.write(", ");
10145                }
10146                self.generate_data_type(dt)?;
10147            }
10148            self.write(")");
10149        }
10150
10151        if df.cascade {
10152            self.write_space();
10153            self.write_keyword("CASCADE");
10154        }
10155
10156        Ok(())
10157    }
10158
10159    fn generate_create_procedure(&mut self, cp: &CreateProcedure) -> Result<()> {
10160        self.write_keyword("CREATE");
10161
10162        if cp.or_replace {
10163            self.write_space();
10164            self.write_keyword("OR REPLACE");
10165        }
10166
10167        self.write_space();
10168        if cp.use_proc_keyword {
10169            self.write_keyword("PROC");
10170        } else {
10171            self.write_keyword("PROCEDURE");
10172        }
10173
10174        if cp.if_not_exists {
10175            self.write_space();
10176            self.write_keyword("IF NOT EXISTS");
10177        }
10178
10179        self.write_space();
10180        self.generate_table(&cp.name)?;
10181        if cp.has_parens {
10182            self.write("(");
10183            self.generate_function_parameters(&cp.parameters)?;
10184            self.write(")");
10185        } else if !cp.parameters.is_empty() {
10186            // TSQL: unparenthesized parameters
10187            self.write_space();
10188            self.generate_function_parameters(&cp.parameters)?;
10189        }
10190
10191        // RETURNS clause (Snowflake)
10192        if let Some(return_type) = &cp.return_type {
10193            self.write_space();
10194            self.write_keyword("RETURNS");
10195            self.write_space();
10196            self.generate_data_type(return_type)?;
10197        }
10198
10199        // EXECUTE AS clause (Snowflake)
10200        if let Some(execute_as) = &cp.execute_as {
10201            self.write_space();
10202            self.write_keyword("EXECUTE AS");
10203            self.write_space();
10204            self.write_keyword(execute_as);
10205        }
10206
10207        if let Some(lang) = &cp.language {
10208            self.write_space();
10209            self.write_keyword("LANGUAGE");
10210            self.write_space();
10211            self.write(lang);
10212        }
10213
10214        if let Some(security) = &cp.security {
10215            self.write_space();
10216            self.write_keyword("SECURITY");
10217            self.write_space();
10218            match security {
10219                FunctionSecurity::Definer => self.write_keyword("DEFINER"),
10220                FunctionSecurity::Invoker => self.write_keyword("INVOKER"),
10221                FunctionSecurity::None => self.write_keyword("NONE"),
10222            }
10223        }
10224
10225        // TSQL WITH options (ENCRYPTION, RECOMPILE, etc.)
10226        if !cp.with_options.is_empty() {
10227            self.write_space();
10228            self.write_keyword("WITH");
10229            self.write_space();
10230            for (i, opt) in cp.with_options.iter().enumerate() {
10231                if i > 0 {
10232                    self.write(", ");
10233                }
10234                self.write(opt);
10235            }
10236        }
10237
10238        if let Some(body) = &cp.body {
10239            self.write_space();
10240            match body {
10241                FunctionBody::Block(block) => {
10242                    self.write_keyword("AS");
10243                    if matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL)) {
10244                        self.write(" BEGIN ");
10245                        self.write(block);
10246                        self.write(" END");
10247                    } else if matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL)) {
10248                        self.write(" $$");
10249                        self.write(block);
10250                        self.write("$$");
10251                    } else {
10252                        // Escape content for single-quoted output
10253                        let escaped = self.escape_block_for_single_quote(block);
10254                        self.write(" '");
10255                        self.write(&escaped);
10256                        self.write("'");
10257                    }
10258                }
10259                FunctionBody::StringLiteral(s) => {
10260                    self.write_keyword("AS");
10261                    self.write(" '");
10262                    self.write(s);
10263                    self.write("'");
10264                }
10265                FunctionBody::Expression(expr) => {
10266                    self.write_keyword("AS");
10267                    self.write_space();
10268                    self.generate_expression(expr)?;
10269                }
10270                FunctionBody::External(name) => {
10271                    self.write_keyword("EXTERNAL NAME");
10272                    self.write(" '");
10273                    self.write(name);
10274                    self.write("'");
10275                }
10276                FunctionBody::Return(expr) => {
10277                    self.write_keyword("RETURN");
10278                    self.write_space();
10279                    self.generate_expression(expr)?;
10280                }
10281                FunctionBody::Statements(stmts) => {
10282                    self.write_keyword("AS");
10283                    self.write(" BEGIN ");
10284                    for (i, stmt) in stmts.iter().enumerate() {
10285                        if i > 0 {
10286                            self.write(" ");
10287                        }
10288                        self.generate_expression(stmt)?;
10289                    }
10290                    self.write(" END");
10291                }
10292                FunctionBody::DollarQuoted { content, tag } => {
10293                    self.write_keyword("AS");
10294                    self.write(" ");
10295                    // Dialects that support dollar-quoted strings: PostgreSQL, Databricks, Redshift, DuckDB
10296                    let supports_dollar_quoting = matches!(
10297                        self.config.dialect,
10298                        Some(crate::dialects::DialectType::PostgreSQL)
10299                            | Some(crate::dialects::DialectType::Databricks)
10300                            | Some(crate::dialects::DialectType::Redshift)
10301                            | Some(crate::dialects::DialectType::DuckDB)
10302                    );
10303                    if supports_dollar_quoting {
10304                        // Output in dollar-quoted format
10305                        self.write("$");
10306                        if let Some(t) = tag {
10307                            self.write(t);
10308                        }
10309                        self.write("$");
10310                        self.write(content);
10311                        self.write("$");
10312                        if let Some(t) = tag {
10313                            self.write(t);
10314                        }
10315                        self.write("$");
10316                    } else {
10317                        // Convert to single-quoted string for other dialects
10318                        let escaped = self.escape_block_for_single_quote(content);
10319                        self.write("'");
10320                        self.write(&escaped);
10321                        self.write("'");
10322                    }
10323                }
10324            }
10325        }
10326
10327        Ok(())
10328    }
10329
10330    fn generate_drop_procedure(&mut self, dp: &DropProcedure) -> Result<()> {
10331        self.write_keyword("DROP PROCEDURE");
10332
10333        if dp.if_exists {
10334            self.write_space();
10335            self.write_keyword("IF EXISTS");
10336        }
10337
10338        self.write_space();
10339        self.generate_table(&dp.name)?;
10340
10341        if let Some(params) = &dp.parameters {
10342            self.write(" (");
10343            for (i, dt) in params.iter().enumerate() {
10344                if i > 0 {
10345                    self.write(", ");
10346                }
10347                self.generate_data_type(dt)?;
10348            }
10349            self.write(")");
10350        }
10351
10352        if dp.cascade {
10353            self.write_space();
10354            self.write_keyword("CASCADE");
10355        }
10356
10357        Ok(())
10358    }
10359
10360    fn generate_create_sequence(&mut self, cs: &CreateSequence) -> Result<()> {
10361        self.write_keyword("CREATE");
10362
10363        if cs.temporary {
10364            self.write_space();
10365            self.write_keyword("TEMPORARY");
10366        }
10367
10368        self.write_space();
10369        self.write_keyword("SEQUENCE");
10370
10371        if cs.if_not_exists {
10372            self.write_space();
10373            self.write_keyword("IF NOT EXISTS");
10374        }
10375
10376        self.write_space();
10377        self.generate_table(&cs.name)?;
10378
10379        // Output COMMENT first (Snowflake convention: COMMENT comes before other properties)
10380        if let Some(comment) = &cs.comment {
10381            self.write_space();
10382            self.write_keyword("COMMENT");
10383            self.write("=");
10384            self.generate_string_literal(comment)?;
10385        }
10386
10387        // If property_order is available, use it to preserve original order
10388        if !cs.property_order.is_empty() {
10389            for prop in &cs.property_order {
10390                match prop {
10391                    SeqPropKind::Start => {
10392                        if let Some(start) = cs.start {
10393                            self.write_space();
10394                            self.write_keyword("START WITH");
10395                            self.write(&format!(" {}", start));
10396                        }
10397                    }
10398                    SeqPropKind::Increment => {
10399                        if let Some(inc) = cs.increment {
10400                            self.write_space();
10401                            self.write_keyword("INCREMENT BY");
10402                            self.write(&format!(" {}", inc));
10403                        }
10404                    }
10405                    SeqPropKind::Minvalue => {
10406                        if let Some(min) = &cs.minvalue {
10407                            self.write_space();
10408                            match min {
10409                                SequenceBound::Value(v) => {
10410                                    self.write_keyword("MINVALUE");
10411                                    self.write(&format!(" {}", v));
10412                                }
10413                                SequenceBound::None => {
10414                                    self.write_keyword("NO MINVALUE");
10415                                }
10416                            }
10417                        }
10418                    }
10419                    SeqPropKind::Maxvalue => {
10420                        if let Some(max) = &cs.maxvalue {
10421                            self.write_space();
10422                            match max {
10423                                SequenceBound::Value(v) => {
10424                                    self.write_keyword("MAXVALUE");
10425                                    self.write(&format!(" {}", v));
10426                                }
10427                                SequenceBound::None => {
10428                                    self.write_keyword("NO MAXVALUE");
10429                                }
10430                            }
10431                        }
10432                    }
10433                    SeqPropKind::Cache => {
10434                        if let Some(cache) = cs.cache {
10435                            self.write_space();
10436                            self.write_keyword("CACHE");
10437                            self.write(&format!(" {}", cache));
10438                        }
10439                    }
10440                    SeqPropKind::Cycle => {
10441                        self.write_space();
10442                        self.write_keyword("CYCLE");
10443                    }
10444                    SeqPropKind::NoCycle => {
10445                        self.write_space();
10446                        self.write_keyword("NO CYCLE");
10447                    }
10448                    SeqPropKind::OwnedBy => {
10449                        if let Some(owned) = &cs.owned_by {
10450                            self.write_space();
10451                            self.write_keyword("OWNED BY");
10452                            self.write_space();
10453                            self.generate_table(owned)?;
10454                        }
10455                    }
10456                    SeqPropKind::Order => {
10457                        self.write_space();
10458                        self.write_keyword("ORDER");
10459                    }
10460                    SeqPropKind::NoOrder => {
10461                        self.write_space();
10462                        self.write_keyword("NOORDER");
10463                    }
10464                    SeqPropKind::Comment => {
10465                        // Comment is handled above, before property_order iteration
10466                    }
10467                }
10468            }
10469        } else {
10470            // Fallback: default order for backwards compatibility
10471            if let Some(inc) = cs.increment {
10472                self.write_space();
10473                self.write_keyword("INCREMENT BY");
10474                self.write(&format!(" {}", inc));
10475            }
10476
10477            if let Some(min) = &cs.minvalue {
10478                self.write_space();
10479                match min {
10480                    SequenceBound::Value(v) => {
10481                        self.write_keyword("MINVALUE");
10482                        self.write(&format!(" {}", v));
10483                    }
10484                    SequenceBound::None => {
10485                        self.write_keyword("NO MINVALUE");
10486                    }
10487                }
10488            }
10489
10490            if let Some(max) = &cs.maxvalue {
10491                self.write_space();
10492                match max {
10493                    SequenceBound::Value(v) => {
10494                        self.write_keyword("MAXVALUE");
10495                        self.write(&format!(" {}", v));
10496                    }
10497                    SequenceBound::None => {
10498                        self.write_keyword("NO MAXVALUE");
10499                    }
10500                }
10501            }
10502
10503            if let Some(start) = cs.start {
10504                self.write_space();
10505                self.write_keyword("START WITH");
10506                self.write(&format!(" {}", start));
10507            }
10508
10509            if let Some(cache) = cs.cache {
10510                self.write_space();
10511                self.write_keyword("CACHE");
10512                self.write(&format!(" {}", cache));
10513            }
10514
10515            if cs.cycle {
10516                self.write_space();
10517                self.write_keyword("CYCLE");
10518            }
10519
10520            if let Some(owned) = &cs.owned_by {
10521                self.write_space();
10522                self.write_keyword("OWNED BY");
10523                self.write_space();
10524                self.generate_table(owned)?;
10525            }
10526        }
10527
10528        Ok(())
10529    }
10530
10531    fn generate_drop_sequence(&mut self, ds: &DropSequence) -> Result<()> {
10532        self.write_keyword("DROP SEQUENCE");
10533
10534        if ds.if_exists {
10535            self.write_space();
10536            self.write_keyword("IF EXISTS");
10537        }
10538
10539        self.write_space();
10540        self.generate_table(&ds.name)?;
10541
10542        if ds.cascade {
10543            self.write_space();
10544            self.write_keyword("CASCADE");
10545        }
10546
10547        Ok(())
10548    }
10549
10550    fn generate_alter_sequence(&mut self, als: &AlterSequence) -> Result<()> {
10551        self.write_keyword("ALTER SEQUENCE");
10552
10553        if als.if_exists {
10554            self.write_space();
10555            self.write_keyword("IF EXISTS");
10556        }
10557
10558        self.write_space();
10559        self.generate_table(&als.name)?;
10560
10561        if let Some(inc) = als.increment {
10562            self.write_space();
10563            self.write_keyword("INCREMENT BY");
10564            self.write(&format!(" {}", inc));
10565        }
10566
10567        if let Some(min) = &als.minvalue {
10568            self.write_space();
10569            match min {
10570                SequenceBound::Value(v) => {
10571                    self.write_keyword("MINVALUE");
10572                    self.write(&format!(" {}", v));
10573                }
10574                SequenceBound::None => {
10575                    self.write_keyword("NO MINVALUE");
10576                }
10577            }
10578        }
10579
10580        if let Some(max) = &als.maxvalue {
10581            self.write_space();
10582            match max {
10583                SequenceBound::Value(v) => {
10584                    self.write_keyword("MAXVALUE");
10585                    self.write(&format!(" {}", v));
10586                }
10587                SequenceBound::None => {
10588                    self.write_keyword("NO MAXVALUE");
10589                }
10590            }
10591        }
10592
10593        if let Some(start) = als.start {
10594            self.write_space();
10595            self.write_keyword("START WITH");
10596            self.write(&format!(" {}", start));
10597        }
10598
10599        if let Some(restart) = &als.restart {
10600            self.write_space();
10601            self.write_keyword("RESTART");
10602            if let Some(val) = restart {
10603                self.write_keyword(" WITH");
10604                self.write(&format!(" {}", val));
10605            }
10606        }
10607
10608        if let Some(cache) = als.cache {
10609            self.write_space();
10610            self.write_keyword("CACHE");
10611            self.write(&format!(" {}", cache));
10612        }
10613
10614        if let Some(cycle) = als.cycle {
10615            self.write_space();
10616            if cycle {
10617                self.write_keyword("CYCLE");
10618            } else {
10619                self.write_keyword("NO CYCLE");
10620            }
10621        }
10622
10623        if let Some(owned) = &als.owned_by {
10624            self.write_space();
10625            self.write_keyword("OWNED BY");
10626            self.write_space();
10627            if let Some(table) = owned {
10628                self.generate_table(table)?;
10629            } else {
10630                self.write_keyword("NONE");
10631            }
10632        }
10633
10634        Ok(())
10635    }
10636
10637    fn generate_create_trigger(&mut self, ct: &CreateTrigger) -> Result<()> {
10638        self.write_keyword("CREATE");
10639
10640        if ct.or_replace {
10641            self.write_space();
10642            self.write_keyword("OR REPLACE");
10643        }
10644
10645        if ct.constraint {
10646            self.write_space();
10647            self.write_keyword("CONSTRAINT");
10648        }
10649
10650        self.write_space();
10651        self.write_keyword("TRIGGER");
10652        self.write_space();
10653        self.generate_identifier(&ct.name)?;
10654
10655        self.write_space();
10656        match ct.timing {
10657            TriggerTiming::Before => self.write_keyword("BEFORE"),
10658            TriggerTiming::After => self.write_keyword("AFTER"),
10659            TriggerTiming::InsteadOf => self.write_keyword("INSTEAD OF"),
10660        }
10661
10662        // Events
10663        for (i, event) in ct.events.iter().enumerate() {
10664            if i > 0 {
10665                self.write_keyword(" OR");
10666            }
10667            self.write_space();
10668            match event {
10669                TriggerEvent::Insert => self.write_keyword("INSERT"),
10670                TriggerEvent::Update(cols) => {
10671                    self.write_keyword("UPDATE");
10672                    if let Some(cols) = cols {
10673                        self.write_space();
10674                        self.write_keyword("OF");
10675                        for (j, col) in cols.iter().enumerate() {
10676                            if j > 0 {
10677                                self.write(",");
10678                            }
10679                            self.write_space();
10680                            self.generate_identifier(col)?;
10681                        }
10682                    }
10683                }
10684                TriggerEvent::Delete => self.write_keyword("DELETE"),
10685                TriggerEvent::Truncate => self.write_keyword("TRUNCATE"),
10686            }
10687        }
10688
10689        self.write_space();
10690        self.write_keyword("ON");
10691        self.write_space();
10692        self.generate_table(&ct.table)?;
10693
10694        // Referencing clause
10695        if let Some(ref_clause) = &ct.referencing {
10696            self.write_space();
10697            self.write_keyword("REFERENCING");
10698            if let Some(old_table) = &ref_clause.old_table {
10699                self.write_space();
10700                self.write_keyword("OLD TABLE AS");
10701                self.write_space();
10702                self.generate_identifier(old_table)?;
10703            }
10704            if let Some(new_table) = &ref_clause.new_table {
10705                self.write_space();
10706                self.write_keyword("NEW TABLE AS");
10707                self.write_space();
10708                self.generate_identifier(new_table)?;
10709            }
10710            if let Some(old_row) = &ref_clause.old_row {
10711                self.write_space();
10712                self.write_keyword("OLD ROW AS");
10713                self.write_space();
10714                self.generate_identifier(old_row)?;
10715            }
10716            if let Some(new_row) = &ref_clause.new_row {
10717                self.write_space();
10718                self.write_keyword("NEW ROW AS");
10719                self.write_space();
10720                self.generate_identifier(new_row)?;
10721            }
10722        }
10723
10724        // Deferrable options for constraint triggers (must come before FOR EACH)
10725        if let Some(deferrable) = ct.deferrable {
10726            self.write_space();
10727            if deferrable {
10728                self.write_keyword("DEFERRABLE");
10729            } else {
10730                self.write_keyword("NOT DEFERRABLE");
10731            }
10732        }
10733
10734        if let Some(initially) = ct.initially_deferred {
10735            self.write_space();
10736            self.write_keyword("INITIALLY");
10737            self.write_space();
10738            if initially {
10739                self.write_keyword("DEFERRED");
10740            } else {
10741                self.write_keyword("IMMEDIATE");
10742            }
10743        }
10744
10745        self.write_space();
10746        self.write_keyword("FOR EACH");
10747        self.write_space();
10748        match ct.for_each {
10749            TriggerForEach::Row => self.write_keyword("ROW"),
10750            TriggerForEach::Statement => self.write_keyword("STATEMENT"),
10751        }
10752
10753        // When clause
10754        if let Some(when) = &ct.when {
10755            self.write_space();
10756            self.write_keyword("WHEN");
10757            self.write(" (");
10758            self.generate_expression(when)?;
10759            self.write(")");
10760        }
10761
10762        // Body
10763        self.write_space();
10764        match &ct.body {
10765            TriggerBody::Execute { function, args } => {
10766                self.write_keyword("EXECUTE FUNCTION");
10767                self.write_space();
10768                self.generate_table(function)?;
10769                self.write("(");
10770                for (i, arg) in args.iter().enumerate() {
10771                    if i > 0 {
10772                        self.write(", ");
10773                    }
10774                    self.generate_expression(arg)?;
10775                }
10776                self.write(")");
10777            }
10778            TriggerBody::Block(block) => {
10779                self.write_keyword("BEGIN");
10780                self.write_space();
10781                self.write(block);
10782                self.write_space();
10783                self.write_keyword("END");
10784            }
10785        }
10786
10787        Ok(())
10788    }
10789
10790    fn generate_drop_trigger(&mut self, dt: &DropTrigger) -> Result<()> {
10791        self.write_keyword("DROP TRIGGER");
10792
10793        if dt.if_exists {
10794            self.write_space();
10795            self.write_keyword("IF EXISTS");
10796        }
10797
10798        self.write_space();
10799        self.generate_identifier(&dt.name)?;
10800
10801        if let Some(table) = &dt.table {
10802            self.write_space();
10803            self.write_keyword("ON");
10804            self.write_space();
10805            self.generate_table(table)?;
10806        }
10807
10808        if dt.cascade {
10809            self.write_space();
10810            self.write_keyword("CASCADE");
10811        }
10812
10813        Ok(())
10814    }
10815
10816    fn generate_create_type(&mut self, ct: &CreateType) -> Result<()> {
10817        self.write_keyword("CREATE TYPE");
10818
10819        if ct.if_not_exists {
10820            self.write_space();
10821            self.write_keyword("IF NOT EXISTS");
10822        }
10823
10824        self.write_space();
10825        self.generate_table(&ct.name)?;
10826
10827        self.write_space();
10828        self.write_keyword("AS");
10829        self.write_space();
10830
10831        match &ct.definition {
10832            TypeDefinition::Enum(values) => {
10833                self.write_keyword("ENUM");
10834                self.write(" (");
10835                for (i, val) in values.iter().enumerate() {
10836                    if i > 0 {
10837                        self.write(", ");
10838                    }
10839                    self.write(&format!("'{}'", val));
10840                }
10841                self.write(")");
10842            }
10843            TypeDefinition::Composite(attrs) => {
10844                self.write("(");
10845                for (i, attr) in attrs.iter().enumerate() {
10846                    if i > 0 {
10847                        self.write(", ");
10848                    }
10849                    self.generate_identifier(&attr.name)?;
10850                    self.write_space();
10851                    self.generate_data_type(&attr.data_type)?;
10852                    if let Some(collate) = &attr.collate {
10853                        self.write_space();
10854                        self.write_keyword("COLLATE");
10855                        self.write_space();
10856                        self.generate_identifier(collate)?;
10857                    }
10858                }
10859                self.write(")");
10860            }
10861            TypeDefinition::Range { subtype, subtype_diff, canonical } => {
10862                self.write_keyword("RANGE");
10863                self.write(" (");
10864                self.write_keyword("SUBTYPE");
10865                self.write(" = ");
10866                self.generate_data_type(subtype)?;
10867                if let Some(diff) = subtype_diff {
10868                    self.write(", ");
10869                    self.write_keyword("SUBTYPE_DIFF");
10870                    self.write(" = ");
10871                    self.write(diff);
10872                }
10873                if let Some(canon) = canonical {
10874                    self.write(", ");
10875                    self.write_keyword("CANONICAL");
10876                    self.write(" = ");
10877                    self.write(canon);
10878                }
10879                self.write(")");
10880            }
10881            TypeDefinition::Base { input, output, internallength } => {
10882                self.write("(");
10883                self.write_keyword("INPUT");
10884                self.write(" = ");
10885                self.write(input);
10886                self.write(", ");
10887                self.write_keyword("OUTPUT");
10888                self.write(" = ");
10889                self.write(output);
10890                if let Some(len) = internallength {
10891                    self.write(", ");
10892                    self.write_keyword("INTERNALLENGTH");
10893                    self.write(" = ");
10894                    self.write(&len.to_string());
10895                }
10896                self.write(")");
10897            }
10898            TypeDefinition::Domain { base_type, default, constraints } => {
10899                self.generate_data_type(base_type)?;
10900                if let Some(def) = default {
10901                    self.write_space();
10902                    self.write_keyword("DEFAULT");
10903                    self.write_space();
10904                    self.generate_expression(def)?;
10905                }
10906                for constr in constraints {
10907                    self.write_space();
10908                    if let Some(name) = &constr.name {
10909                        self.write_keyword("CONSTRAINT");
10910                        self.write_space();
10911                        self.generate_identifier(name)?;
10912                        self.write_space();
10913                    }
10914                    self.write_keyword("CHECK");
10915                    self.write(" (");
10916                    self.generate_expression(&constr.check)?;
10917                    self.write(")");
10918                }
10919            }
10920        }
10921
10922        Ok(())
10923    }
10924
10925    fn generate_drop_type(&mut self, dt: &DropType) -> Result<()> {
10926        self.write_keyword("DROP TYPE");
10927
10928        if dt.if_exists {
10929            self.write_space();
10930            self.write_keyword("IF EXISTS");
10931        }
10932
10933        self.write_space();
10934        self.generate_table(&dt.name)?;
10935
10936        if dt.cascade {
10937            self.write_space();
10938            self.write_keyword("CASCADE");
10939        }
10940
10941        Ok(())
10942    }
10943
10944    fn generate_describe(&mut self, d: &Describe) -> Result<()> {
10945        // Athena: DESCRIBE uses Hive engine (backticks)
10946        let saved_athena_hive_context = self.athena_hive_context;
10947        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
10948            self.athena_hive_context = true;
10949        }
10950
10951        // Output leading comments before DESCRIBE
10952        for comment in &d.leading_comments {
10953            self.write_formatted_comment(comment);
10954            self.write(" ");
10955        }
10956
10957        self.write_keyword("DESCRIBE");
10958
10959        if d.extended {
10960            self.write_space();
10961            self.write_keyword("EXTENDED");
10962        } else if d.formatted {
10963            self.write_space();
10964            self.write_keyword("FORMATTED");
10965        }
10966
10967        // Output style like ANALYZE, HISTORY
10968        if let Some(ref style) = d.style {
10969            self.write_space();
10970            self.write_keyword(style);
10971        }
10972
10973        // Handle object kind (TABLE, VIEW) based on dialect
10974        let should_output_kind = match self.config.dialect {
10975            // Spark doesn't use TABLE/VIEW after DESCRIBE
10976            Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => false,
10977            // Snowflake always includes TABLE
10978            Some(DialectType::Snowflake) => true,
10979            _ => d.kind.is_some(),
10980        };
10981        if should_output_kind {
10982            if let Some(ref kind) = d.kind {
10983                self.write_space();
10984                self.write_keyword(kind);
10985            } else if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
10986                self.write_space();
10987                self.write_keyword("TABLE");
10988            }
10989        }
10990
10991        self.write_space();
10992        self.generate_expression(&d.target)?;
10993
10994        // Output PARTITION clause if present (the Partition expression outputs its own PARTITION keyword)
10995        if let Some(ref partition) = d.partition {
10996            self.write_space();
10997            self.generate_expression(partition)?;
10998        }
10999
11000        // Output properties like type=stage
11001        for (name, value) in &d.properties {
11002            self.write_space();
11003            self.write(name);
11004            self.write("=");
11005            self.write(value);
11006        }
11007
11008        // Restore Athena Hive context
11009        self.athena_hive_context = saved_athena_hive_context;
11010
11011        Ok(())
11012    }
11013
11014    /// Generate SHOW statement (Snowflake, MySQL, etc.)
11015    /// SHOW [TERSE] <object_type> [HISTORY] [LIKE pattern] [IN <scope>] [STARTS WITH pattern] [LIMIT n] [FROM object]
11016    fn generate_show(&mut self, s: &Show) -> Result<()> {
11017        self.write_keyword("SHOW");
11018        self.write_space();
11019
11020        // TERSE keyword - but not for PRIMARY KEYS, UNIQUE KEYS, IMPORTED KEYS
11021        // where TERSE is syntactically valid but has no effect on output
11022        let show_terse = s.terse && !matches!(
11023            s.this.as_str(),
11024            "PRIMARY KEYS" | "UNIQUE KEYS" | "IMPORTED KEYS"
11025        );
11026        if show_terse {
11027            self.write_keyword("TERSE");
11028            self.write_space();
11029        }
11030
11031        // Object type (USERS, TABLES, DATABASES, etc.)
11032        self.write_keyword(&s.this);
11033
11034        // Target identifier (MySQL: engine name in SHOW ENGINE, preserved case)
11035        if let Some(ref target_expr) = s.target {
11036            self.write_space();
11037            self.generate_expression(target_expr)?;
11038        }
11039
11040        // HISTORY keyword
11041        if s.history {
11042            self.write_space();
11043            self.write_keyword("HISTORY");
11044        }
11045
11046        // FOR target (MySQL: SHOW GRANTS FOR foo, SHOW PROFILE ... FOR QUERY 5)
11047        if let Some(ref for_target) = s.for_target {
11048            self.write_space();
11049            self.write_keyword("FOR");
11050            self.write_space();
11051            self.generate_expression(for_target)?;
11052        }
11053
11054        // Determine ordering based on dialect:
11055        // - Snowflake: LIKE, IN, STARTS WITH, LIMIT, FROM
11056        // - MySQL: IN, FROM, LIKE (when FROM is present)
11057        use crate::dialects::DialectType;
11058        let is_snowflake = matches!(self.config.dialect, Some(DialectType::Snowflake));
11059
11060        if !is_snowflake && s.from.is_some() {
11061            // MySQL ordering: IN, FROM, LIKE
11062
11063            // IN scope_kind [scope]
11064            if let Some(ref scope_kind) = s.scope_kind {
11065                self.write_space();
11066                self.write_keyword("IN");
11067                self.write_space();
11068                self.write_keyword(scope_kind);
11069                if let Some(ref scope) = s.scope {
11070                    self.write_space();
11071                    self.generate_expression(scope)?;
11072                }
11073            } else if let Some(ref scope) = s.scope {
11074                self.write_space();
11075                self.write_keyword("IN");
11076                self.write_space();
11077                self.generate_expression(scope)?;
11078            }
11079
11080            // FROM clause
11081            if let Some(ref from) = s.from {
11082                self.write_space();
11083                self.write_keyword("FROM");
11084                self.write_space();
11085                self.generate_expression(from)?;
11086            }
11087
11088            // Second FROM clause (db name)
11089            if let Some(ref db) = s.db {
11090                self.write_space();
11091                self.write_keyword("FROM");
11092                self.write_space();
11093                self.generate_expression(db)?;
11094            }
11095
11096            // LIKE pattern
11097            if let Some(ref like) = s.like {
11098                self.write_space();
11099                self.write_keyword("LIKE");
11100                self.write_space();
11101                self.generate_expression(like)?;
11102            }
11103        } else {
11104            // Snowflake ordering: LIKE, IN, STARTS WITH, LIMIT, FROM
11105
11106            // LIKE pattern
11107            if let Some(ref like) = s.like {
11108                self.write_space();
11109                self.write_keyword("LIKE");
11110                self.write_space();
11111                self.generate_expression(like)?;
11112            }
11113
11114            // IN scope_kind [scope]
11115            if let Some(ref scope_kind) = s.scope_kind {
11116                self.write_space();
11117                self.write_keyword("IN");
11118                self.write_space();
11119                self.write_keyword(scope_kind);
11120                if let Some(ref scope) = s.scope {
11121                    self.write_space();
11122                    self.generate_expression(scope)?;
11123                }
11124            } else if let Some(ref scope) = s.scope {
11125                self.write_space();
11126                self.write_keyword("IN");
11127                self.write_space();
11128                self.generate_expression(scope)?;
11129            }
11130        }
11131
11132        // STARTS WITH pattern
11133        if let Some(ref starts_with) = s.starts_with {
11134            self.write_space();
11135            self.write_keyword("STARTS WITH");
11136            self.write_space();
11137            self.generate_expression(starts_with)?;
11138        }
11139
11140        // LIMIT clause
11141        if let Some(ref limit) = s.limit {
11142            self.write_space();
11143            self.generate_limit(limit)?;
11144        }
11145
11146        // FROM clause (for Snowflake, FROM comes after STARTS WITH and LIMIT)
11147        if is_snowflake {
11148            if let Some(ref from) = s.from {
11149                self.write_space();
11150                self.write_keyword("FROM");
11151                self.write_space();
11152                self.generate_expression(from)?;
11153            }
11154        }
11155
11156        // WHERE clause (MySQL: SHOW STATUS WHERE condition)
11157        if let Some(ref where_clause) = s.where_clause {
11158            self.write_space();
11159            self.write_keyword("WHERE");
11160            self.write_space();
11161            self.generate_expression(where_clause)?;
11162        }
11163
11164        // MUTEX/STATUS suffix (MySQL: SHOW ENGINE foo STATUS/MUTEX)
11165        if let Some(is_mutex) = s.mutex {
11166            self.write_space();
11167            if is_mutex {
11168                self.write_keyword("MUTEX");
11169            } else {
11170                self.write_keyword("STATUS");
11171            }
11172        }
11173
11174        // WITH PRIVILEGES clause (Snowflake: SHOW ... WITH PRIVILEGES USAGE, MODIFY)
11175        if !s.privileges.is_empty() {
11176            self.write_space();
11177            self.write_keyword("WITH PRIVILEGES");
11178            self.write_space();
11179            for (i, priv_name) in s.privileges.iter().enumerate() {
11180                if i > 0 {
11181                    self.write(", ");
11182                }
11183                self.write_keyword(priv_name);
11184            }
11185        }
11186
11187        Ok(())
11188    }
11189
11190    // ==================== End DDL Generation ====================
11191
11192    fn generate_literal(&mut self, lit: &Literal) -> Result<()> {
11193        use crate::dialects::DialectType;
11194        match lit {
11195            Literal::String(s) => {
11196                self.generate_string_literal(s)?;
11197            }
11198            Literal::Number(n) => {
11199                if matches!(self.config.dialect, Some(DialectType::MySQL))
11200                    && n.len() > 2
11201                    && (n.starts_with("0x") || n.starts_with("0X"))
11202                    && !n[2..].chars().all(|c| c.is_ascii_hexdigit())
11203                {
11204                    return self.generate_identifier(&Identifier {
11205                        name: n.clone(),
11206                        quoted: true,
11207                        trailing_comments: Vec::new(),
11208                    });
11209                }
11210                // Normalize numbers starting with decimal point to have leading zero
11211                // e.g., .25 -> 0.25 (matches sqlglot behavior)
11212                if n.starts_with('.') {
11213                    self.write("0");
11214                    self.write(n);
11215                } else if n.starts_with("-.") {
11216                    // Handle negative numbers like -.25 -> -0.25
11217                    self.write("-0");
11218                    self.write(&n[1..]);
11219                } else {
11220                    self.write(n);
11221                }
11222            }
11223            Literal::HexString(h) => {
11224                // Most dialects use lowercase x'...' for hex literals; Spark/Databricks/Teradata use uppercase X'...'
11225                match self.config.dialect {
11226                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Teradata) => self.write("X'"),
11227                    _ => self.write("x'"),
11228                }
11229                self.write(h);
11230                self.write("'");
11231            }
11232            Literal::HexNumber(h) => {
11233                // Hex number (0xA) - integer in hex notation (from BigQuery)
11234                // For BigQuery, output as 0xHEX
11235                // For other dialects, convert to decimal integer
11236                match self.config.dialect {
11237                    Some(DialectType::BigQuery) => {
11238                        self.write("0x");
11239                        self.write(h);
11240                    }
11241                    _ => {
11242                        // Convert hex to decimal
11243                        if let Ok(val) = u64::from_str_radix(h, 16) {
11244                            self.write(&val.to_string());
11245                        } else {
11246                            // Fallback: keep as 0x notation
11247                            self.write("0x");
11248                            self.write(h);
11249                        }
11250                    }
11251                }
11252            }
11253            Literal::BitString(b) => {
11254                // Bit string B'0101...'
11255                self.write("B'");
11256                self.write(b);
11257                self.write("'");
11258            }
11259            Literal::ByteString(b) => {
11260                // Byte string b'...' (BigQuery style)
11261                self.write("b'");
11262                // Escape special characters for output
11263                self.write_escaped_byte_string(b);
11264                self.write("'");
11265            }
11266            Literal::NationalString(s) => {
11267                // N'string' is supported by TSQL, Oracle, MySQL, and generic SQL
11268                // Other dialects strip the N prefix and output as regular string
11269                let keep_n_prefix = matches!(self.config.dialect,
11270                    Some(DialectType::TSQL) | Some(DialectType::Oracle) | Some(DialectType::MySQL) | None
11271                );
11272                if keep_n_prefix {
11273                    self.write("N'");
11274                } else {
11275                    self.write("'");
11276                }
11277                self.write(s);
11278                self.write("'");
11279            }
11280            Literal::Date(d) => {
11281                self.generate_date_literal(d)?;
11282            }
11283            Literal::Time(t) => {
11284                self.generate_time_literal(t)?;
11285            }
11286            Literal::Timestamp(ts) => {
11287                self.generate_timestamp_literal(ts)?;
11288            }
11289            Literal::Datetime(dt) => {
11290                self.generate_datetime_literal(dt)?;
11291            }
11292            Literal::TripleQuotedString(s, _quote_char) => {
11293                // For BigQuery and other dialects that don't support triple-quote, normalize to regular strings
11294                if matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)
11295                    | Some(crate::dialects::DialectType::DuckDB)
11296                    | Some(crate::dialects::DialectType::Snowflake)
11297                    | Some(crate::dialects::DialectType::Spark)
11298                    | Some(crate::dialects::DialectType::Hive)
11299                    | Some(crate::dialects::DialectType::Presto)
11300                    | Some(crate::dialects::DialectType::Trino)
11301                    | Some(crate::dialects::DialectType::PostgreSQL)
11302                    | Some(crate::dialects::DialectType::MySQL)
11303                    | Some(crate::dialects::DialectType::Redshift)
11304                    | Some(crate::dialects::DialectType::TSQL)
11305                    | Some(crate::dialects::DialectType::Oracle)
11306                    | Some(crate::dialects::DialectType::ClickHouse)
11307                    | Some(crate::dialects::DialectType::Databricks)
11308                    | Some(crate::dialects::DialectType::SQLite)
11309                ) {
11310                    self.generate_string_literal(s)?;
11311                } else {
11312                    // Preserve triple-quoted string syntax for generic/unknown dialects
11313                    let quotes = format!("{0}{0}{0}", _quote_char);
11314                    self.write(&quotes);
11315                    self.write(s);
11316                    self.write(&quotes);
11317                }
11318            }
11319            Literal::EscapeString(s) => {
11320                // PostgreSQL escape string: e'...' or E'...'
11321                // Token text format is "e:content" or "E:content"
11322                // Normalize escape sequences: \' -> '' (standard SQL doubled quote)
11323                use crate::dialects::DialectType;
11324                let content = if let Some(c) = s.strip_prefix("e:") {
11325                    c
11326                } else if let Some(c) = s.strip_prefix("E:") {
11327                    c
11328                } else {
11329                    s.as_str()
11330                };
11331
11332                // MySQL: output the content without quotes or prefix
11333                if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::TiDB)) {
11334                    self.write(content);
11335                } else {
11336                    // Some dialects use lowercase e' prefix
11337                    let prefix = if matches!(
11338                        self.config.dialect,
11339                        Some(DialectType::SingleStore)
11340                            | Some(DialectType::DuckDB)
11341                            | Some(DialectType::PostgreSQL)
11342                            | Some(DialectType::CockroachDB)
11343                            | Some(DialectType::Materialize)
11344                            | Some(DialectType::RisingWave)
11345                    ) {
11346                        "e'"
11347                    } else {
11348                        "E'"
11349                    };
11350
11351                    // Normalize \' to '' for output
11352                    let normalized = content.replace("\\'", "''");
11353                    self.write(prefix);
11354                    self.write(&normalized);
11355                    self.write("'");
11356                }
11357            }
11358            Literal::DollarString(s) => {
11359                // Convert dollar-quoted strings to single-quoted strings
11360                // (like Python sqlglot's rawstring_sql)
11361                use crate::dialects::DialectType;
11362                // Extract content from tag\x00content format
11363                let (_tag, content) = crate::tokens::parse_dollar_string_token(s);
11364                // Step 1: Escape backslashes if the dialect uses backslash as a string escape
11365                let escape_backslash = matches!(
11366                    self.config.dialect,
11367                    Some(DialectType::Snowflake)
11368                );
11369                // Step 2: Determine quote escaping style
11370                // Snowflake: ' -> \' (backslash escape)
11371                // PostgreSQL, DuckDB, others: ' -> '' (doubled quote)
11372                let use_backslash_quote = matches!(
11373                    self.config.dialect,
11374                    Some(DialectType::Snowflake)
11375                );
11376
11377                let mut escaped = String::with_capacity(content.len() + 4);
11378                for ch in content.chars() {
11379                    if escape_backslash && ch == '\\' {
11380                        // Escape backslash first (before quote escaping)
11381                        escaped.push('\\');
11382                        escaped.push('\\');
11383                    } else if ch == '\'' {
11384                        if use_backslash_quote {
11385                            escaped.push('\\');
11386                            escaped.push('\'');
11387                        } else {
11388                            escaped.push('\'');
11389                            escaped.push('\'');
11390                        }
11391                    } else {
11392                        escaped.push(ch);
11393                    }
11394                }
11395                self.write("'");
11396                self.write(&escaped);
11397                self.write("'");
11398            }
11399            Literal::RawString(s) => {
11400                // Raw strings (r"..." or r'...') contain literal backslashes.
11401                // When converting to a regular string, this follows Python sqlglot's rawstring_sql:
11402                // 1. If \\ is in STRING_ESCAPES, double all backslashes
11403                // 2. Apply ESCAPED_SEQUENCES for special chars (but NOT for backslash itself)
11404                // 3. Escape quotes using STRING_ESCAPES[0] + quote_char
11405                use crate::dialects::DialectType;
11406
11407                // Dialects where \\ is in STRING_ESCAPES (backslashes need doubling)
11408                let escape_backslash = matches!(
11409                    self.config.dialect,
11410                    Some(DialectType::BigQuery)
11411                        | Some(DialectType::MySQL)
11412                        | Some(DialectType::SingleStore)
11413                        | Some(DialectType::TiDB)
11414                        | Some(DialectType::Hive)
11415                        | Some(DialectType::Spark)
11416                       
11417                        | Some(DialectType::Databricks)
11418                        | Some(DialectType::Drill)
11419                        | Some(DialectType::Snowflake)
11420                        | Some(DialectType::Redshift)
11421                        | Some(DialectType::ClickHouse)
11422                );
11423
11424                // Dialects where backslash is the PRIMARY string escape (STRING_ESCAPES[0] = "\\")
11425                // These escape quotes as \' instead of ''
11426                let backslash_escapes_quote = matches!(
11427                    self.config.dialect,
11428                    Some(DialectType::BigQuery)
11429                        | Some(DialectType::Hive)
11430                        | Some(DialectType::Spark)
11431                       
11432                        | Some(DialectType::Databricks)
11433                        | Some(DialectType::Drill)
11434                        | Some(DialectType::Snowflake)
11435                        | Some(DialectType::Redshift)
11436                );
11437
11438                // Whether this dialect supports escaped sequences (ESCAPED_SEQUENCES mapping)
11439                // This is True when \\ is in STRING_ESCAPES (same as escape_backslash)
11440                let supports_escape_sequences = escape_backslash;
11441
11442                let mut escaped = String::with_capacity(s.len() + 4);
11443                for ch in s.chars() {
11444                    if escape_backslash && ch == '\\' {
11445                        // Double the backslash for the target dialect
11446                        escaped.push('\\');
11447                        escaped.push('\\');
11448                    } else if ch == '\'' {
11449                        if backslash_escapes_quote {
11450                            // Use backslash to escape the quote: \'
11451                            escaped.push('\\');
11452                            escaped.push('\'');
11453                        } else {
11454                            // Use SQL standard quote doubling: ''
11455                            escaped.push('\'');
11456                            escaped.push('\'');
11457                        }
11458                    } else if supports_escape_sequences {
11459                        // Apply ESCAPED_SEQUENCES mapping for special chars
11460                        // (escape_backslash=False in rawstring_sql, so \\ is NOT escaped here)
11461                        match ch {
11462                            '\n' => { escaped.push('\\'); escaped.push('n'); }
11463                            '\r' => { escaped.push('\\'); escaped.push('r'); }
11464                            '\t' => { escaped.push('\\'); escaped.push('t'); }
11465                            '\x07' => { escaped.push('\\'); escaped.push('a'); }
11466                            '\x08' => { escaped.push('\\'); escaped.push('b'); }
11467                            '\x0C' => { escaped.push('\\'); escaped.push('f'); }
11468                            '\x0B' => { escaped.push('\\'); escaped.push('v'); }
11469                            _ => escaped.push(ch),
11470                        }
11471                    } else {
11472                        escaped.push(ch);
11473                    }
11474                }
11475                self.write("'");
11476                self.write(&escaped);
11477                self.write("'");
11478            }
11479        }
11480        Ok(())
11481    }
11482
11483    /// Generate a DATE literal with dialect-specific formatting
11484    fn generate_date_literal(&mut self, d: &str) -> Result<()> {
11485        use crate::dialects::DialectType;
11486
11487        match self.config.dialect {
11488            // SQL Server uses CONVERT or CAST
11489            Some(DialectType::TSQL) => {
11490                self.write("CAST('");
11491                self.write(d);
11492                self.write("' AS DATE)");
11493            }
11494            // BigQuery uses CAST syntax for type literals
11495            // DATE 'value' -> CAST('value' AS DATE)
11496            Some(DialectType::BigQuery) => {
11497                self.write("CAST('");
11498                self.write(d);
11499                self.write("' AS DATE)");
11500            }
11501            // Exasol uses CAST syntax for DATE literals
11502            // DATE 'value' -> CAST('value' AS DATE)
11503            Some(DialectType::Exasol) => {
11504                self.write("CAST('");
11505                self.write(d);
11506                self.write("' AS DATE)");
11507            }
11508            // Snowflake uses CAST syntax for DATE literals
11509            // DATE 'value' -> CAST('value' AS DATE)
11510            Some(DialectType::Snowflake) => {
11511                self.write("CAST('");
11512                self.write(d);
11513                self.write("' AS DATE)");
11514            }
11515            // PostgreSQL, MySQL, Redshift: DATE 'value' -> CAST('value' AS DATE)
11516            Some(DialectType::PostgreSQL) | Some(DialectType::MySQL)
11517            | Some(DialectType::SingleStore) | Some(DialectType::TiDB)
11518            | Some(DialectType::Redshift) => {
11519                self.write("CAST('");
11520                self.write(d);
11521                self.write("' AS DATE)");
11522            }
11523            // DuckDB, Presto, Trino, Spark: DATE 'value' -> CAST('value' AS DATE)
11524            Some(DialectType::DuckDB) | Some(DialectType::Presto) | Some(DialectType::Trino)
11525            | Some(DialectType::Athena) | Some(DialectType::Spark)
11526            | Some(DialectType::Databricks) | Some(DialectType::Hive) => {
11527                self.write("CAST('");
11528                self.write(d);
11529                self.write("' AS DATE)");
11530            }
11531            // Oracle: DATE 'value' -> TO_DATE('value', 'YYYY-MM-DD')
11532            Some(DialectType::Oracle) => {
11533                self.write("TO_DATE('");
11534                self.write(d);
11535                self.write("', 'YYYY-MM-DD')");
11536            }
11537            // Standard SQL: DATE '...'
11538            _ => {
11539                self.write_keyword("DATE");
11540                self.write(" '");
11541                self.write(d);
11542                self.write("'");
11543            }
11544        }
11545        Ok(())
11546    }
11547
11548    /// Generate a TIME literal with dialect-specific formatting
11549    fn generate_time_literal(&mut self, t: &str) -> Result<()> {
11550        use crate::dialects::DialectType;
11551
11552        match self.config.dialect {
11553            // SQL Server uses CONVERT or CAST
11554            Some(DialectType::TSQL) => {
11555                self.write("CAST('");
11556                self.write(t);
11557                self.write("' AS TIME)");
11558            }
11559            // Standard SQL: TIME '...'
11560            _ => {
11561                self.write_keyword("TIME");
11562                self.write(" '");
11563                self.write(t);
11564                self.write("'");
11565            }
11566        }
11567        Ok(())
11568    }
11569
11570    /// Generate a date expression for Dremio, converting DATE literals to CAST
11571    fn generate_dremio_date_expression(&mut self, expr: &Expression) -> Result<()> {
11572        use crate::expressions::Literal;
11573
11574        match expr {
11575            Expression::Literal(Literal::Date(d)) => {
11576                // DATE 'value' -> CAST('value' AS DATE)
11577                self.write("CAST('");
11578                self.write(d);
11579                self.write("' AS DATE)");
11580            }
11581            _ => {
11582                // For all other expressions, generate normally
11583                self.generate_expression(expr)?;
11584            }
11585        }
11586        Ok(())
11587    }
11588
11589    /// Generate a TIMESTAMP literal with dialect-specific formatting
11590    fn generate_timestamp_literal(&mut self, ts: &str) -> Result<()> {
11591        use crate::dialects::DialectType;
11592
11593        match self.config.dialect {
11594            // SQL Server uses CONVERT or CAST
11595            Some(DialectType::TSQL) => {
11596                self.write("CAST('");
11597                self.write(ts);
11598                self.write("' AS DATETIME2)");
11599            }
11600            // BigQuery uses CAST syntax for type literals
11601            // TIMESTAMP 'value' -> CAST('value' AS TIMESTAMP)
11602            Some(DialectType::BigQuery) => {
11603                self.write("CAST('");
11604                self.write(ts);
11605                self.write("' AS TIMESTAMP)");
11606            }
11607            // Snowflake uses CAST syntax for TIMESTAMP literals
11608            // TIMESTAMP 'value' -> CAST('value' AS TIMESTAMP)
11609            Some(DialectType::Snowflake) => {
11610                self.write("CAST('");
11611                self.write(ts);
11612                self.write("' AS TIMESTAMP)");
11613            }
11614            // Dremio uses CAST syntax for TIMESTAMP literals
11615            // TIMESTAMP 'value' -> CAST('value' AS TIMESTAMP)
11616            Some(DialectType::Dremio) => {
11617                self.write("CAST('");
11618                self.write(ts);
11619                self.write("' AS TIMESTAMP)");
11620            }
11621            // Exasol uses CAST syntax for TIMESTAMP literals
11622            // TIMESTAMP 'value' -> CAST('value' AS TIMESTAMP)
11623            Some(DialectType::Exasol) => {
11624                self.write("CAST('");
11625                self.write(ts);
11626                self.write("' AS TIMESTAMP)");
11627            }
11628            // Oracle prefers TO_TIMESTAMP function call
11629            // TIMESTAMP 'value' -> TO_TIMESTAMP('value', 'YYYY-MM-DD HH24:MI:SS.FF6')
11630            Some(DialectType::Oracle) => {
11631                self.write("TO_TIMESTAMP('");
11632                self.write(ts);
11633                self.write("', 'YYYY-MM-DD HH24:MI:SS.FF6')");
11634            }
11635            // Presto/Trino: always use CAST for TIMESTAMP literals
11636            Some(DialectType::Presto) | Some(DialectType::Trino) => {
11637                if Self::timestamp_has_timezone(ts) {
11638                    self.write("CAST('");
11639                    self.write(ts);
11640                    self.write("' AS TIMESTAMP WITH TIME ZONE)");
11641                } else {
11642                    self.write("CAST('");
11643                    self.write(ts);
11644                    self.write("' AS TIMESTAMP)");
11645                }
11646            }
11647            // ClickHouse: CAST('...' AS Nullable(DateTime))
11648            Some(DialectType::ClickHouse) => {
11649                self.write("CAST('");
11650                self.write(ts);
11651                self.write("' AS Nullable(DateTime))");
11652            }
11653            // Spark: CAST('...' AS TIMESTAMP)
11654            Some(DialectType::Spark) => {
11655                self.write("CAST('");
11656                self.write(ts);
11657                self.write("' AS TIMESTAMP)");
11658            }
11659            // Redshift: CAST('...' AS TIMESTAMP) for regular timestamps,
11660            // but TIMESTAMP '...' for special values like 'epoch'
11661            Some(DialectType::Redshift) => {
11662                if ts == "epoch" {
11663                    self.write_keyword("TIMESTAMP");
11664                    self.write(" '");
11665                    self.write(ts);
11666                    self.write("'");
11667                } else {
11668                    self.write("CAST('");
11669                    self.write(ts);
11670                    self.write("' AS TIMESTAMP)");
11671                }
11672            }
11673            // PostgreSQL, Hive, DuckDB, etc.: CAST('...' AS TIMESTAMP)
11674            Some(DialectType::PostgreSQL) | Some(DialectType::Hive) |
11675            Some(DialectType::SQLite) | Some(DialectType::DuckDB) |
11676            Some(DialectType::Athena) | Some(DialectType::Drill) |
11677            Some(DialectType::Teradata) => {
11678                self.write("CAST('");
11679                self.write(ts);
11680                self.write("' AS TIMESTAMP)");
11681            }
11682            // MySQL/StarRocks: CAST('...' AS DATETIME)
11683            Some(DialectType::MySQL) | Some(DialectType::StarRocks) | Some(DialectType::Doris) => {
11684                self.write("CAST('");
11685                self.write(ts);
11686                self.write("' AS DATETIME)");
11687            }
11688            // Databricks: CAST('...' AS TIMESTAMP_NTZ)
11689            Some(DialectType::Databricks) => {
11690                self.write("CAST('");
11691                self.write(ts);
11692                self.write("' AS TIMESTAMP_NTZ)");
11693            }
11694            // Standard SQL: TIMESTAMP '...'
11695            _ => {
11696                self.write_keyword("TIMESTAMP");
11697                self.write(" '");
11698                self.write(ts);
11699                self.write("'");
11700            }
11701        }
11702        Ok(())
11703    }
11704
11705    /// Check if a timestamp string contains a timezone identifier
11706    /// This detects IANA timezone names like Europe/Prague, America/New_York, etc.
11707    fn timestamp_has_timezone(ts: &str) -> bool {
11708        // Check for common IANA timezone patterns: Continent/City format
11709        // Examples: Europe/Prague, America/New_York, Asia/Tokyo, etc.
11710        // Also handles: UTC, GMT, Etc/GMT+0, etc.
11711        let ts_lower = ts.to_lowercase();
11712
11713        // Check for Continent/City pattern (most common)
11714        let continent_prefixes = [
11715            "africa/", "america/", "antarctica/", "arctic/", "asia/",
11716            "atlantic/", "australia/", "europe/", "indian/", "pacific/",
11717            "etc/", "brazil/", "canada/", "chile/", "mexico/", "us/",
11718        ];
11719
11720        for prefix in &continent_prefixes {
11721            if ts_lower.contains(prefix) {
11722                return true;
11723            }
11724        }
11725
11726        // Check for standalone timezone abbreviations at the end
11727        // These typically appear after the time portion
11728        let tz_abbrevs = [
11729            " utc", " gmt", " cet", " cest", " eet", " eest", " wet", " west",
11730            " est", " edt", " cst", " cdt", " mst", " mdt", " pst", " pdt",
11731            " ist", " bst", " jst", " kst", " hkt", " sgt", " aest", " aedt",
11732            " acst", " acdt", " awst",
11733        ];
11734
11735        for abbrev in &tz_abbrevs {
11736            if ts_lower.ends_with(abbrev) {
11737                return true;
11738            }
11739        }
11740
11741        // Check for numeric timezone offsets: +N, -N, +NN:NN, -NN:NN
11742        // Examples: "2012-10-31 01:00 -2", "2012-10-31 01:00 +02:00"
11743        // Look for pattern: space followed by + or - and digits (optionally with :)
11744        let trimmed = ts.trim();
11745        if let Some(last_space) = trimmed.rfind(' ') {
11746            let suffix = &trimmed[last_space + 1..];
11747            if (suffix.starts_with('+') || suffix.starts_with('-')) && suffix.len() > 1 {
11748                // Check if rest is numeric (possibly with : for hh:mm format)
11749                let rest = &suffix[1..];
11750                if rest.chars().all(|c| c.is_ascii_digit() || c == ':') {
11751                    return true;
11752                }
11753            }
11754        }
11755
11756        false
11757    }
11758
11759    /// Generate a DATETIME literal with dialect-specific formatting
11760    fn generate_datetime_literal(&mut self, dt: &str) -> Result<()> {
11761        use crate::dialects::DialectType;
11762
11763        match self.config.dialect {
11764            // BigQuery uses CAST syntax for type literals
11765            // DATETIME 'value' -> CAST('value' AS DATETIME)
11766            Some(DialectType::BigQuery) => {
11767                self.write("CAST('");
11768                self.write(dt);
11769                self.write("' AS DATETIME)");
11770            }
11771            // DuckDB: DATETIME -> CAST('value' AS TIMESTAMP)
11772            Some(DialectType::DuckDB) => {
11773                self.write("CAST('");
11774                self.write(dt);
11775                self.write("' AS TIMESTAMP)");
11776            }
11777            // DATETIME is primarily a BigQuery type
11778            // Output as DATETIME '...' for dialects that support it
11779            _ => {
11780                self.write_keyword("DATETIME");
11781                self.write(" '");
11782                self.write(dt);
11783                self.write("'");
11784            }
11785        }
11786        Ok(())
11787    }
11788
11789    /// Generate a string literal with dialect-specific escaping
11790    fn generate_string_literal(&mut self, s: &str) -> Result<()> {
11791        use crate::dialects::DialectType;
11792
11793        match self.config.dialect {
11794            // MySQL/Hive: Uses SQL standard quote escaping ('') for quotes,
11795            // and backslash escaping for special characters like newlines
11796            // Hive STRING_ESCAPES = ["\\"] - uses backslash escapes
11797            Some(DialectType::Hive)
11798            | Some(DialectType::Spark)
11799           
11800            | Some(DialectType::Databricks)
11801            | Some(DialectType::Drill) => {
11802                // Hive/Spark use backslash escaping for quotes (\') and special chars
11803                self.write("'");
11804                for c in s.chars() {
11805                    match c {
11806                        '\'' => self.write("\\'"),
11807                        '\\' => self.write("\\\\"),
11808                        '\n' => self.write("\\n"),
11809                        '\r' => self.write("\\r"),
11810                        '\t' => self.write("\\t"),
11811                        '\0' => self.write("\\0"),
11812                        _ => self.output.push(c),
11813                    }
11814                }
11815                self.write("'");
11816            }
11817            Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB) => {
11818                self.write("'");
11819                for c in s.chars() {
11820                    match c {
11821                        // MySQL uses SQL standard quote doubling
11822                        '\'' => self.write("''"),
11823                        '\\' => self.write("\\\\"),
11824                        '\n' => self.write("\\n"),
11825                        '\r' => self.write("\\r"),
11826                        '\t' => self.write("\\t"),
11827                        // sqlglot writes a literal NUL for this case
11828                        '\0' => self.output.push('\0'),
11829                        _ => self.output.push(c),
11830                    }
11831                }
11832                self.write("'");
11833            }
11834            // BigQuery: Uses backslash escaping
11835            Some(DialectType::BigQuery) => {
11836                self.write("'");
11837                for c in s.chars() {
11838                    match c {
11839                        '\'' => self.write("\\'"),
11840                        '\\' => self.write("\\\\"),
11841                        '\n' => self.write("\\n"),
11842                        '\r' => self.write("\\r"),
11843                        '\t' => self.write("\\t"),
11844                        '\0' => self.write("\\0"),
11845                        '\x07' => self.write("\\a"),
11846                        '\x08' => self.write("\\b"),
11847                        '\x0C' => self.write("\\f"),
11848                        '\x0B' => self.write("\\v"),
11849                        _ => self.output.push(c),
11850                    }
11851                }
11852                self.write("'");
11853            }
11854            // Athena: Uses different escaping for DDL (Hive) vs DML (Trino)
11855            // In Hive context (DDL): backslash escaping for single quotes (\') and backslashes (\\)
11856            // In Trino context (DML): SQL-standard escaping ('') and literal backslashes
11857            Some(DialectType::Athena) => {
11858                if self.athena_hive_context {
11859                    // Hive-style: backslash escaping
11860                    self.write("'");
11861                    for c in s.chars() {
11862                        match c {
11863                            '\'' => self.write("\\'"),
11864                            '\\' => self.write("\\\\"),
11865                            '\n' => self.write("\\n"),
11866                            '\r' => self.write("\\r"),
11867                            '\t' => self.write("\\t"),
11868                            '\0' => self.write("\\0"),
11869                            _ => self.output.push(c),
11870                        }
11871                    }
11872                    self.write("'");
11873                } else {
11874                    // Trino-style: SQL-standard escaping, preserve backslashes
11875                    self.write("'");
11876                    for c in s.chars() {
11877                        match c {
11878                            '\'' => self.write("''"),
11879                            // Preserve backslashes literally (no re-escaping)
11880                            _ => self.output.push(c),
11881                        }
11882                    }
11883                    self.write("'");
11884                }
11885            }
11886            // Snowflake: Uses backslash escaping (STRING_ESCAPES = ["\\", "'"])
11887            // The tokenizer preserves backslash escape sequences literally (e.g., input '\\'
11888            // becomes string value '\\'), so we should NOT re-escape backslashes.
11889            // We only need to escape single quotes.
11890            Some(DialectType::Snowflake) => {
11891                self.write("'");
11892                for c in s.chars() {
11893                    match c {
11894                        '\'' => self.write("\\'"),
11895                        // Backslashes are already escaped in the tokenized string, don't re-escape
11896                        // Only escape special characters that might not have been escaped
11897                        '\n' => self.write("\\n"),
11898                        '\r' => self.write("\\r"),
11899                        '\t' => self.write("\\t"),
11900                        _ => self.output.push(c),
11901                    }
11902                }
11903                self.write("'");
11904            }
11905            // PostgreSQL: Output special characters as literal chars in strings (no E-string prefix)
11906            Some(DialectType::PostgreSQL) => {
11907                self.write("'");
11908                for c in s.chars() {
11909                    match c {
11910                        '\'' => self.write("''"),
11911                        _ => self.output.push(c),
11912                    }
11913                }
11914                self.write("'");
11915            }
11916            // Redshift: Uses backslash escaping for single quotes
11917            Some(DialectType::Redshift) => {
11918                self.write("'");
11919                for c in s.chars() {
11920                    match c {
11921                        '\'' => self.write("\\'"),
11922                        _ => self.output.push(c),
11923                    }
11924                }
11925                self.write("'");
11926            }
11927            // Oracle: Uses standard double single-quote escaping
11928            Some(DialectType::Oracle) => {
11929                self.write("'");
11930                self.write(&s.replace('\'', "''"));
11931                self.write("'");
11932            }
11933            // ClickHouse: Uses SQL-standard quote doubling ('') for quotes,
11934            // backslash escaping for backslashes and special characters
11935            Some(DialectType::ClickHouse) => {
11936                self.write("'");
11937                for c in s.chars() {
11938                    match c {
11939                        '\'' => self.write("''"),
11940                        '\\' => self.write("\\\\"),
11941                        '\n' => self.write("\\n"),
11942                        '\r' => self.write("\\r"),
11943                        '\t' => self.write("\\t"),
11944                        '\0' => self.write("\\0"),
11945                        _ => self.output.push(c),
11946                    }
11947                }
11948                self.write("'");
11949            }
11950            // Default: SQL standard double single quotes (works for most dialects)
11951            // PostgreSQL, Snowflake, DuckDB, TSQL, etc.
11952            _ => {
11953                self.write("'");
11954                self.write(&s.replace('\'', "''"));
11955                self.write("'");
11956            }
11957        }
11958        Ok(())
11959    }
11960
11961    /// Write a byte string with proper escaping for BigQuery-style byte literals
11962    /// Escapes characters as \xNN hex escapes where needed
11963    fn write_escaped_byte_string(&mut self, s: &str) {
11964        for c in s.chars() {
11965            match c {
11966                // Escape single quotes
11967                '\'' => self.write("\\'"),
11968                // Escape backslashes
11969                '\\' => self.write("\\\\"),
11970                // Keep all printable characters (including non-ASCII) as-is
11971                _ if !c.is_control() => self.output.push(c),
11972                // Escape control characters as hex
11973                _ => {
11974                    let byte = c as u32;
11975                    if byte < 256 {
11976                        self.write(&format!("\\x{:02x}", byte));
11977                    } else {
11978                        // For unicode characters, write each UTF-8 byte
11979                        for b in c.to_string().as_bytes() {
11980                            self.write(&format!("\\x{:02x}", b));
11981                        }
11982                    }
11983                }
11984            }
11985        }
11986    }
11987
11988    fn generate_boolean(&mut self, b: &BooleanLiteral) -> Result<()> {
11989        use crate::dialects::DialectType;
11990
11991        // Different dialects have different boolean literal formats
11992        match self.config.dialect {
11993            // SQL Server typically uses 1/0 for boolean literals in many contexts
11994            // However, TRUE/FALSE also works in modern versions
11995            Some(DialectType::TSQL) => {
11996                self.write(if b.value { "1" } else { "0" });
11997            }
11998            // Oracle traditionally uses 1/0 (no native boolean until recent versions)
11999            Some(DialectType::Oracle) => {
12000                self.write(if b.value { "1" } else { "0" });
12001            }
12002            // MySQL accepts TRUE/FALSE as aliases for 1/0
12003            Some(DialectType::MySQL) => {
12004                self.write_keyword(if b.value { "TRUE" } else { "FALSE" });
12005            }
12006            // Most other dialects support TRUE/FALSE
12007            _ => {
12008                self.write_keyword(if b.value { "TRUE" } else { "FALSE" });
12009            }
12010        }
12011        Ok(())
12012    }
12013
12014    /// Generate an identifier that's used as an alias name
12015    /// This quotes reserved keywords in addition to already-quoted identifiers
12016    fn generate_alias_identifier(&mut self, id: &Identifier) -> Result<()> {
12017        let name = &id.name;
12018        let quote_style = &self.config.identifier_quote_style;
12019
12020        // For aliases, quote if:
12021        // 1. The identifier was explicitly quoted in the source
12022        // 2. The identifier is a reserved keyword for the current dialect
12023        let needs_quoting = id.quoted || self.is_reserved_keyword(name);
12024
12025        // Normalize identifier if configured
12026        let output_name = if self.config.normalize_identifiers && !id.quoted {
12027            name.to_lowercase()
12028        } else {
12029            name.to_string()
12030        };
12031
12032        if needs_quoting {
12033            // Escape any quote characters within the identifier
12034            let escaped_name = if quote_style.start == quote_style.end {
12035                output_name.replace(
12036                    quote_style.end,
12037                    &format!("{}{}", quote_style.end, quote_style.end)
12038                )
12039            } else {
12040                output_name.replace(
12041                    quote_style.end,
12042                    &format!("{}{}", quote_style.end, quote_style.end)
12043                )
12044            };
12045            self.write(&format!("{}{}{}", quote_style.start, escaped_name, quote_style.end));
12046        } else {
12047            self.write(&output_name);
12048        }
12049
12050        // Output trailing comments
12051        for comment in &id.trailing_comments {
12052            self.write(" ");
12053            self.write(comment);
12054        }
12055        Ok(())
12056    }
12057
12058    fn generate_identifier(&mut self, id: &Identifier) -> Result<()> {
12059        use crate::dialects::DialectType;
12060
12061        let name = &id.name;
12062
12063        // For Athena, use backticks in Hive context, double quotes in Trino context
12064        let quote_style = if matches!(self.config.dialect, Some(DialectType::Athena)) && self.athena_hive_context {
12065            &IdentifierQuoteStyle::BACKTICK
12066        } else {
12067            &self.config.identifier_quote_style
12068        };
12069
12070        // Quote if:
12071        // 1. The identifier was explicitly quoted in the source
12072        // 2. The identifier is a reserved keyword for the current dialect
12073        // 3. The config says to always quote identifiers (e.g., Athena/Presto)
12074        // This matches Python sqlglot's identifier_sql behavior
12075        // Also quote identifiers starting with digits if the target dialect doesn't support them
12076        let starts_with_digit = name.chars().next().map_or(false, |c| c.is_ascii_digit());
12077        let needs_digit_quoting = starts_with_digit && !self.config.identifiers_can_start_with_digit && self.config.dialect.is_some();
12078        let mysql_invalid_hex_identifier = matches!(self.config.dialect, Some(DialectType::MySQL))
12079            && name.len() > 2
12080            && (name.starts_with("0x") || name.starts_with("0X"))
12081            && !name[2..].chars().all(|c| c.is_ascii_hexdigit());
12082        let needs_quoting = id.quoted
12083            || self.is_reserved_keyword(name)
12084            || self.config.always_quote_identifiers
12085            || needs_digit_quoting
12086            || mysql_invalid_hex_identifier;
12087
12088        // Check for MySQL index column prefix length: name(16) or name(16) ASC/DESC
12089        // When quoted, we need to output `name`(16) not `name(16)`
12090        let (base_name, suffix) = if needs_quoting {
12091            // Try to extract prefix length from identifier: name(number) or name(number) ASC/DESC
12092            if let Some(paren_pos) = name.find('(') {
12093                let base = &name[..paren_pos];
12094                let rest = &name[paren_pos..];
12095                // Verify it looks like (digits) or (digits) ASC/DESC
12096                if rest.starts_with('(') && (rest.ends_with(')') || rest.ends_with(") ASC") || rest.ends_with(") DESC")) {
12097                    // Check if content between parens is all digits
12098                    let close_paren = rest.find(')').unwrap_or(rest.len());
12099                    let inside = &rest[1..close_paren];
12100                    if inside.chars().all(|c| c.is_ascii_digit()) {
12101                        (base.to_string(), rest.to_string())
12102                    } else {
12103                        (name.to_string(), String::new())
12104                    }
12105                } else {
12106                    (name.to_string(), String::new())
12107                }
12108            } else if name.ends_with(" ASC") {
12109                let base = &name[..name.len() - 4];
12110                (base.to_string(), " ASC".to_string())
12111            } else if name.ends_with(" DESC") {
12112                let base = &name[..name.len() - 5];
12113                (base.to_string(), " DESC".to_string())
12114            } else {
12115                (name.to_string(), String::new())
12116            }
12117        } else {
12118            (name.to_string(), String::new())
12119        };
12120
12121        // Normalize identifier if configured, with special handling for Exasol
12122        // Exasol uses UPPERCASE normalization strategy, so reserved keywords that need quoting
12123        // should be uppercased when not already quoted (to match Python sqlglot behavior)
12124        let output_name = if self.config.normalize_identifiers && !id.quoted {
12125            base_name.to_lowercase()
12126        } else if matches!(self.config.dialect, Some(DialectType::Exasol)) && !id.quoted && self.is_reserved_keyword(name) {
12127            // Exasol: uppercase reserved keywords when quoting them
12128            // This matches Python sqlglot's behavior with NORMALIZATION_STRATEGY = UPPERCASE
12129            base_name.to_uppercase()
12130        } else {
12131            base_name
12132        };
12133
12134        if needs_quoting {
12135            // Escape any quote characters within the identifier
12136            let escaped_name = if quote_style.start == quote_style.end {
12137                // Same start/end char (e.g., " or `) - double the quote char
12138                output_name.replace(
12139                    quote_style.end,
12140                    &format!("{}{}", quote_style.end, quote_style.end)
12141                )
12142            } else {
12143                // Different start/end (e.g., [ and ]) - escape only the end char
12144                output_name.replace(
12145                    quote_style.end,
12146                    &format!("{}{}", quote_style.end, quote_style.end)
12147                )
12148            };
12149            self.write(&format!("{}{}{}{}", quote_style.start, escaped_name, quote_style.end, suffix));
12150        } else {
12151            self.write(&output_name);
12152        }
12153
12154        // Output trailing comments
12155        for comment in &id.trailing_comments {
12156            self.write(" ");
12157            self.write(comment);
12158        }
12159        Ok(())
12160    }
12161
12162    fn generate_column(&mut self, col: &Column) -> Result<()> {
12163        use crate::dialects::DialectType;
12164
12165        if let Some(table) = &col.table {
12166            // Exasol special case: LOCAL as column table prefix should NOT be quoted
12167            // LOCAL is a special keyword in Exasol for referencing aliases from the current scope
12168            // Only applies when: dialect is Exasol, name is "LOCAL" (case-insensitive), and not already quoted
12169            let is_exasol_local_prefix = matches!(self.config.dialect, Some(DialectType::Exasol))
12170                && !table.quoted
12171                && table.name.eq_ignore_ascii_case("LOCAL");
12172
12173            if is_exasol_local_prefix {
12174                // Write LOCAL unquoted (this is special Exasol syntax, not a table reference)
12175                self.write("LOCAL");
12176            } else {
12177                self.generate_identifier(table)?;
12178            }
12179            self.write(".");
12180        }
12181        self.generate_identifier(&col.name)?;
12182        // Oracle-style join marker (+)
12183        // Only output if dialect supports it (Oracle, Exasol)
12184        if col.join_mark && self.config.supports_column_join_marks {
12185            self.write(" (+)");
12186        }
12187        // Output trailing comments
12188        for comment in &col.trailing_comments {
12189            self.write_space();
12190            self.write(comment);
12191        }
12192        Ok(())
12193    }
12194
12195    /// Generate a pseudocolumn (Oracle ROWNUM, ROWID, LEVEL, etc.)
12196    /// Pseudocolumns should NEVER be quoted, as quoting breaks them in Oracle
12197    fn generate_pseudocolumn(&mut self, pc: &Pseudocolumn) -> Result<()> {
12198        use crate::dialects::DialectType;
12199        use crate::expressions::PseudocolumnType;
12200
12201        // SYSDATE -> CURRENT_TIMESTAMP for non-Oracle/Redshift dialects
12202        if pc.kind == PseudocolumnType::Sysdate
12203            && !matches!(self.config.dialect, Some(DialectType::Oracle) | Some(DialectType::Redshift) | None)
12204        {
12205            self.write_keyword("CURRENT_TIMESTAMP");
12206            // Add () for dialects that expect it
12207            if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::ClickHouse)
12208                | Some(DialectType::Spark) | Some(DialectType::Databricks)
12209                | Some(DialectType::Hive)) {
12210                self.write("()");
12211            }
12212        } else {
12213            self.write(pc.kind.as_str());
12214        }
12215        Ok(())
12216    }
12217
12218    /// Generate CONNECT BY clause (Oracle hierarchical queries)
12219    fn generate_connect(&mut self, connect: &Connect) -> Result<()> {
12220        use crate::dialects::DialectType;
12221
12222        // Generate native CONNECT BY for Oracle and Snowflake
12223        // For other dialects, add a comment noting manual conversion needed
12224        let supports_connect_by = matches!(self.config.dialect, Some(DialectType::Oracle) | Some(DialectType::Snowflake));
12225
12226        if !supports_connect_by && self.config.dialect.is_some() {
12227            // Add comment for unsupported dialects
12228            if self.config.pretty {
12229                self.write_newline();
12230            } else {
12231                self.write_space();
12232            }
12233            self.write("/* CONNECT BY requires manual conversion to recursive CTE */");
12234        }
12235
12236        // Generate START WITH if present (before CONNECT BY)
12237        if let Some(start) = &connect.start {
12238            if self.config.pretty {
12239                self.write_newline();
12240            } else {
12241                self.write_space();
12242            }
12243            self.write_keyword("START WITH");
12244            self.write_space();
12245            self.generate_expression(start)?;
12246        }
12247
12248        // Generate CONNECT BY
12249        if self.config.pretty {
12250            self.write_newline();
12251        } else {
12252            self.write_space();
12253        }
12254        self.write_keyword("CONNECT BY");
12255        if connect.nocycle {
12256            self.write_space();
12257            self.write_keyword("NOCYCLE");
12258        }
12259        self.write_space();
12260        self.generate_expression(&connect.connect)?;
12261
12262        Ok(())
12263    }
12264
12265    /// Generate Connect expression (for Expression::Connect variant)
12266    fn generate_connect_expr(&mut self, connect: &Connect) -> Result<()> {
12267        self.generate_connect(connect)
12268    }
12269
12270    /// Generate PRIOR expression
12271    fn generate_prior(&mut self, prior: &Prior) -> Result<()> {
12272        self.write_keyword("PRIOR");
12273        self.write_space();
12274        self.generate_expression(&prior.this)?;
12275        Ok(())
12276    }
12277
12278    /// Generate CONNECT_BY_ROOT function
12279    /// Syntax: CONNECT_BY_ROOT column (no parentheses)
12280    fn generate_connect_by_root(&mut self, cbr: &ConnectByRoot) -> Result<()> {
12281        self.write_keyword("CONNECT_BY_ROOT");
12282        self.write_space();
12283        self.generate_expression(&cbr.this)?;
12284        Ok(())
12285    }
12286
12287    /// Generate MATCH_RECOGNIZE clause
12288    fn generate_match_recognize(&mut self, mr: &MatchRecognize) -> Result<()> {
12289        use crate::dialects::DialectType;
12290
12291        // MATCH_RECOGNIZE is supported in Oracle, Snowflake, Presto, and Trino
12292        let supports_match_recognize = matches!(
12293            self.config.dialect,
12294            Some(DialectType::Oracle) | Some(DialectType::Snowflake) | Some(DialectType::Presto) | Some(DialectType::Trino)
12295        );
12296
12297        // Generate the source table first
12298        if let Some(source) = &mr.this {
12299            self.generate_expression(source)?;
12300        }
12301
12302        if !supports_match_recognize {
12303            self.write("/* MATCH_RECOGNIZE not supported in this dialect */");
12304            return Ok(());
12305        }
12306
12307        // In pretty mode, MATCH_RECOGNIZE should be on a new line
12308        if self.config.pretty {
12309            self.write_newline();
12310        } else {
12311            self.write_space();
12312        }
12313
12314        self.write_keyword("MATCH_RECOGNIZE");
12315        self.write(" (");
12316
12317        if self.config.pretty {
12318            self.indent_level += 1;
12319        }
12320
12321        let mut needs_separator = false;
12322
12323        // PARTITION BY
12324        if let Some(partition_by) = &mr.partition_by {
12325            if !partition_by.is_empty() {
12326                if self.config.pretty {
12327                    self.write_newline();
12328                    self.write_indent();
12329                }
12330                self.write_keyword("PARTITION BY");
12331                self.write_space();
12332                for (i, expr) in partition_by.iter().enumerate() {
12333                    if i > 0 {
12334                        self.write(", ");
12335                    }
12336                    self.generate_expression(expr)?;
12337                }
12338                needs_separator = true;
12339            }
12340        }
12341
12342        // ORDER BY
12343        if let Some(order_by) = &mr.order_by {
12344            if !order_by.is_empty() {
12345                if needs_separator {
12346                    if self.config.pretty {
12347                        self.write_newline();
12348                        self.write_indent();
12349                    } else {
12350                        self.write_space();
12351                    }
12352                } else if self.config.pretty {
12353                    self.write_newline();
12354                    self.write_indent();
12355                }
12356                self.write_keyword("ORDER BY");
12357                // In pretty mode, put each ORDER BY column on a new indented line
12358                if self.config.pretty {
12359                    self.indent_level += 1;
12360                    for (i, ordered) in order_by.iter().enumerate() {
12361                        if i > 0 {
12362                            self.write(",");
12363                        }
12364                        self.write_newline();
12365                        self.write_indent();
12366                        self.generate_ordered(ordered)?;
12367                    }
12368                    self.indent_level -= 1;
12369                } else {
12370                    self.write_space();
12371                    for (i, ordered) in order_by.iter().enumerate() {
12372                        if i > 0 {
12373                            self.write(", ");
12374                        }
12375                        self.generate_ordered(ordered)?;
12376                    }
12377                }
12378                needs_separator = true;
12379            }
12380        }
12381
12382        // MEASURES
12383        if let Some(measures) = &mr.measures {
12384            if !measures.is_empty() {
12385                if needs_separator {
12386                    if self.config.pretty {
12387                        self.write_newline();
12388                        self.write_indent();
12389                    } else {
12390                        self.write_space();
12391                    }
12392                } else if self.config.pretty {
12393                    self.write_newline();
12394                    self.write_indent();
12395                }
12396                self.write_keyword("MEASURES");
12397                // In pretty mode, put each MEASURE on a new indented line
12398                if self.config.pretty {
12399                    self.indent_level += 1;
12400                    for (i, measure) in measures.iter().enumerate() {
12401                        if i > 0 {
12402                            self.write(",");
12403                        }
12404                        self.write_newline();
12405                        self.write_indent();
12406                        // Handle RUNNING/FINAL prefix
12407                        if let Some(semantics) = &measure.window_frame {
12408                            match semantics {
12409                                MatchRecognizeSemantics::Running => {
12410                                    self.write_keyword("RUNNING");
12411                                    self.write_space();
12412                                }
12413                                MatchRecognizeSemantics::Final => {
12414                                    self.write_keyword("FINAL");
12415                                    self.write_space();
12416                                }
12417                            }
12418                        }
12419                        self.generate_expression(&measure.this)?;
12420                    }
12421                    self.indent_level -= 1;
12422                } else {
12423                    self.write_space();
12424                    for (i, measure) in measures.iter().enumerate() {
12425                        if i > 0 {
12426                            self.write(", ");
12427                        }
12428                        // Handle RUNNING/FINAL prefix
12429                        if let Some(semantics) = &measure.window_frame {
12430                            match semantics {
12431                                MatchRecognizeSemantics::Running => {
12432                                    self.write_keyword("RUNNING");
12433                                    self.write_space();
12434                                }
12435                                MatchRecognizeSemantics::Final => {
12436                                    self.write_keyword("FINAL");
12437                                    self.write_space();
12438                                }
12439                            }
12440                        }
12441                        self.generate_expression(&measure.this)?;
12442                    }
12443                }
12444                needs_separator = true;
12445            }
12446        }
12447
12448        // Row semantics (ONE ROW PER MATCH, ALL ROWS PER MATCH, etc.)
12449        if let Some(rows) = &mr.rows {
12450            if needs_separator {
12451                if self.config.pretty {
12452                    self.write_newline();
12453                    self.write_indent();
12454                } else {
12455                    self.write_space();
12456                }
12457            } else if self.config.pretty {
12458                self.write_newline();
12459                self.write_indent();
12460            }
12461            match rows {
12462                MatchRecognizeRows::OneRowPerMatch => {
12463                    self.write_keyword("ONE ROW PER MATCH");
12464                }
12465                MatchRecognizeRows::AllRowsPerMatch => {
12466                    self.write_keyword("ALL ROWS PER MATCH");
12467                }
12468                MatchRecognizeRows::AllRowsPerMatchShowEmptyMatches => {
12469                    self.write_keyword("ALL ROWS PER MATCH SHOW EMPTY MATCHES");
12470                }
12471                MatchRecognizeRows::AllRowsPerMatchOmitEmptyMatches => {
12472                    self.write_keyword("ALL ROWS PER MATCH OMIT EMPTY MATCHES");
12473                }
12474                MatchRecognizeRows::AllRowsPerMatchWithUnmatchedRows => {
12475                    self.write_keyword("ALL ROWS PER MATCH WITH UNMATCHED ROWS");
12476                }
12477            }
12478            needs_separator = true;
12479        }
12480
12481        // AFTER MATCH SKIP
12482        if let Some(after) = &mr.after {
12483            if needs_separator {
12484                if self.config.pretty {
12485                    self.write_newline();
12486                    self.write_indent();
12487                } else {
12488                    self.write_space();
12489                }
12490            } else if self.config.pretty {
12491                self.write_newline();
12492                self.write_indent();
12493            }
12494            match after {
12495                MatchRecognizeAfter::PastLastRow => {
12496                    self.write_keyword("AFTER MATCH SKIP PAST LAST ROW");
12497                }
12498                MatchRecognizeAfter::ToNextRow => {
12499                    self.write_keyword("AFTER MATCH SKIP TO NEXT ROW");
12500                }
12501                MatchRecognizeAfter::ToFirst(ident) => {
12502                    self.write_keyword("AFTER MATCH SKIP TO FIRST");
12503                    self.write_space();
12504                    self.generate_identifier(ident)?;
12505                }
12506                MatchRecognizeAfter::ToLast(ident) => {
12507                    self.write_keyword("AFTER MATCH SKIP TO LAST");
12508                    self.write_space();
12509                    self.generate_identifier(ident)?;
12510                }
12511            }
12512            needs_separator = true;
12513        }
12514
12515        // PATTERN
12516        if let Some(pattern) = &mr.pattern {
12517            if needs_separator {
12518                if self.config.pretty {
12519                    self.write_newline();
12520                    self.write_indent();
12521                } else {
12522                    self.write_space();
12523                }
12524            } else if self.config.pretty {
12525                self.write_newline();
12526                self.write_indent();
12527            }
12528            self.write_keyword("PATTERN");
12529            self.write_space();
12530            self.write("(");
12531            self.write(pattern);
12532            self.write(")");
12533            needs_separator = true;
12534        }
12535
12536        // DEFINE
12537        if let Some(define) = &mr.define {
12538            if !define.is_empty() {
12539                if needs_separator {
12540                    if self.config.pretty {
12541                        self.write_newline();
12542                        self.write_indent();
12543                    } else {
12544                        self.write_space();
12545                    }
12546                } else if self.config.pretty {
12547                    self.write_newline();
12548                    self.write_indent();
12549                }
12550                self.write_keyword("DEFINE");
12551                // In pretty mode, put each DEFINE on a new indented line
12552                if self.config.pretty {
12553                    self.indent_level += 1;
12554                    for (i, (name, expr)) in define.iter().enumerate() {
12555                        if i > 0 {
12556                            self.write(",");
12557                        }
12558                        self.write_newline();
12559                        self.write_indent();
12560                        self.generate_identifier(name)?;
12561                        self.write(" AS ");
12562                        self.generate_expression(expr)?;
12563                    }
12564                    self.indent_level -= 1;
12565                } else {
12566                    self.write_space();
12567                    for (i, (name, expr)) in define.iter().enumerate() {
12568                        if i > 0 {
12569                            self.write(", ");
12570                        }
12571                        self.generate_identifier(name)?;
12572                        self.write(" AS ");
12573                        self.generate_expression(expr)?;
12574                    }
12575                }
12576            }
12577        }
12578
12579        if self.config.pretty {
12580            self.indent_level -= 1;
12581            self.write_newline();
12582        }
12583        self.write(")");
12584
12585        // Alias - only include AS if it was explicitly present in the input
12586        if let Some(alias) = &mr.alias {
12587            self.write(" ");
12588            if mr.alias_explicit_as {
12589                self.write_keyword("AS");
12590                self.write(" ");
12591            }
12592            self.generate_identifier(alias)?;
12593        }
12594
12595        Ok(())
12596    }
12597
12598    /// Generate a query hint /*+ ... */
12599    fn generate_hint(&mut self, hint: &Hint) -> Result<()> {
12600        use crate::dialects::DialectType;
12601
12602        // Output hints for dialects that support them, or when no dialect is specified (identity tests)
12603        let supports_hints = matches!(
12604            self.config.dialect,
12605            None |  // No dialect = preserve everything
12606            Some(DialectType::Oracle) | Some(DialectType::MySQL) |
12607            Some(DialectType::Spark) | Some(DialectType::Hive) |
12608            Some(DialectType::Databricks) | Some(DialectType::PostgreSQL)
12609        );
12610
12611        if !supports_hints || hint.expressions.is_empty() {
12612            return Ok(());
12613        }
12614
12615        // First, expand raw hint text into individual hint strings
12616        // This handles the case where the parser stored multiple hints as a single raw string
12617        let mut hint_strings: Vec<String> = Vec::new();
12618        for expr in &hint.expressions {
12619            match expr {
12620                HintExpression::Raw(text) => {
12621                    // Parse raw hint text into individual hint function calls
12622                    let parsed = self.parse_raw_hint_text(text);
12623                    hint_strings.extend(parsed);
12624                }
12625                _ => {
12626                    hint_strings.push(self.hint_expression_to_string(expr)?);
12627                }
12628            }
12629        }
12630
12631        // In pretty mode with multiple hints, always use multiline format
12632        // This matches Python sqlglot's behavior where expressions() with default dynamic=False
12633        // always joins with newlines in pretty mode
12634        let use_multiline = self.config.pretty && hint_strings.len() > 1;
12635
12636        if use_multiline {
12637            // Pretty print with each hint on its own line
12638            self.write(" /*+ ");
12639            for (i, hint_str) in hint_strings.iter().enumerate() {
12640                if i > 0 {
12641                    self.write_newline();
12642                    self.write("  "); // 2-space indent within hint block
12643                }
12644                self.write(hint_str);
12645            }
12646            self.write(" */");
12647        } else {
12648            // Single line format
12649            self.write(" /*+ ");
12650            let sep = match self.config.dialect {
12651                Some(DialectType::Spark) | Some(DialectType::Databricks) => ", ",
12652                _ => " ",
12653            };
12654            for (i, hint_str) in hint_strings.iter().enumerate() {
12655                if i > 0 {
12656                    self.write(sep);
12657                }
12658                self.write(hint_str);
12659            }
12660            self.write(" */");
12661        }
12662
12663        Ok(())
12664    }
12665
12666    /// Parse raw hint text into individual hint function calls
12667    /// e.g., "LEADING(a b) USE_NL(c)" -> ["LEADING(a b)", "USE_NL(c)"]
12668    /// If the hint contains unparseable content (like SQL keywords), return as single raw string
12669    fn parse_raw_hint_text(&self, text: &str) -> Vec<String> {
12670        let mut results = Vec::new();
12671        let mut chars = text.chars().peekable();
12672        let mut current = String::new();
12673        let mut paren_depth = 0;
12674        let mut has_unparseable_content = false;
12675        let mut position_after_last_function = 0;
12676        let mut char_position = 0;
12677
12678        while let Some(c) = chars.next() {
12679            char_position += c.len_utf8();
12680            match c {
12681                '(' => {
12682                    paren_depth += 1;
12683                    current.push(c);
12684                }
12685                ')' => {
12686                    paren_depth -= 1;
12687                    current.push(c);
12688                    // When we close the outer parenthesis, we've completed a hint function
12689                    if paren_depth == 0 {
12690                        let trimmed = current.trim().to_string();
12691                        if !trimmed.is_empty() {
12692                            // Format this hint for pretty printing if needed
12693                            let formatted = self.format_hint_function(&trimmed);
12694                            results.push(formatted);
12695                        }
12696                        current.clear();
12697                        position_after_last_function = char_position;
12698                    }
12699                }
12700                ' ' | '\t' | '\n' | ',' if paren_depth == 0 => {
12701                    // Space/comma/whitespace outside parentheses - skip
12702                }
12703                _ if paren_depth == 0 => {
12704                    // Character outside parentheses - accumulate for potential hint name
12705                    current.push(c);
12706                }
12707                _ => {
12708                    current.push(c);
12709                }
12710            }
12711        }
12712
12713        // Check if there's remaining text after the last function call
12714        let remaining_text = text[position_after_last_function..].trim();
12715        if !remaining_text.is_empty() {
12716            // Check if it looks like valid hint function names
12717            // Valid hint identifiers typically are uppercase alphanumeric with underscores
12718            // If we see multiple words without parens, it's likely unparseable
12719            let words: Vec<&str> = remaining_text.split_whitespace().collect();
12720            let looks_like_hint_functions = words.iter().all(|word| {
12721                // A valid hint name followed by opening paren, or a standalone uppercase identifier
12722                word.contains('(') || (word.chars().all(|c| c.is_ascii_uppercase() || c == '_'))
12723            });
12724
12725            if !looks_like_hint_functions && words.len() > 1 {
12726                has_unparseable_content = true;
12727            }
12728        }
12729
12730        // If we detected unparseable content (like SQL keywords), return the whole hint as-is
12731        if has_unparseable_content {
12732            return vec![text.trim().to_string()];
12733        }
12734
12735        // If we couldn't parse anything, return the original text as a single hint
12736        if results.is_empty() {
12737            results.push(text.trim().to_string());
12738        }
12739
12740        results
12741    }
12742
12743    /// Format a hint function for pretty printing
12744    /// e.g., "LEADING(aaa bbb ccc ddd)" -> multiline if args are too wide
12745    fn format_hint_function(&self, hint: &str) -> String {
12746        if !self.config.pretty {
12747            return hint.to_string();
12748        }
12749
12750        // Try to parse NAME(args) pattern
12751        if let Some(paren_pos) = hint.find('(') {
12752            if hint.ends_with(')') {
12753                let name = &hint[..paren_pos];
12754                let args_str = &hint[paren_pos + 1..hint.len() - 1];
12755
12756                // Parse arguments (space-separated for Oracle hints)
12757                let args: Vec<&str> = args_str.split_whitespace().collect();
12758
12759                // Calculate total width of arguments
12760                let total_args_width: usize = args.iter().map(|s| s.len()).sum::<usize>()
12761                    + args.len().saturating_sub(1); // spaces between args
12762
12763                // If too wide, format on multiple lines
12764                if total_args_width > self.config.max_text_width && !args.is_empty() {
12765                    let mut result = format!("{}(\n", name);
12766                    for arg in &args {
12767                        result.push_str("    "); // 4-space indent for args
12768                        result.push_str(arg);
12769                        result.push('\n');
12770                    }
12771                    result.push_str("  )"); // 2-space indent for closing paren
12772                    return result;
12773                }
12774            }
12775        }
12776
12777        hint.to_string()
12778    }
12779
12780    /// Convert a hint expression to a string, handling multiline formatting for long arguments
12781    fn hint_expression_to_string(&mut self, expr: &HintExpression) -> Result<String> {
12782        match expr {
12783            HintExpression::Function { name, args } => {
12784                // Generate each argument to a string
12785                let arg_strings: Vec<String> = args.iter()
12786                    .map(|arg| {
12787                        let mut gen = Generator::with_config(self.config.clone());
12788                        gen.generate_expression(arg)?;
12789                        Ok(gen.output)
12790                    })
12791                    .collect::<Result<Vec<_>>>()?;
12792
12793                // Oracle hints use space-separated arguments, not comma-separated
12794                let total_args_width: usize = arg_strings.iter().map(|s| s.len()).sum::<usize>()
12795                    + arg_strings.len().saturating_sub(1); // spaces between args
12796
12797                // Check if function args need multiline formatting
12798                // Use too_wide check for argument formatting
12799                let args_multiline = self.config.pretty
12800                    && total_args_width > self.config.max_text_width;
12801
12802                if args_multiline && !arg_strings.is_empty() {
12803                    // Multiline format for long argument lists
12804                    let mut result = format!("{}(\n", name);
12805                    for arg_str in &arg_strings {
12806                        result.push_str("    "); // 4-space indent for args
12807                        result.push_str(arg_str);
12808                        result.push('\n');
12809                    }
12810                    result.push_str("  )"); // 2-space indent for closing paren
12811                    Ok(result)
12812                } else {
12813                    // Single line format with space-separated args (Oracle style)
12814                    let args_str = arg_strings.join(" ");
12815                    Ok(format!("{}({})", name, args_str))
12816                }
12817            }
12818            HintExpression::Identifier(name) => Ok(name.clone()),
12819            HintExpression::Raw(text) => {
12820                // For pretty printing, try to format the raw text
12821                if self.config.pretty {
12822                    Ok(self.format_hint_function(text))
12823                } else {
12824                    Ok(text.clone())
12825                }
12826            }
12827        }
12828    }
12829
12830    fn generate_table(&mut self, table: &TableRef) -> Result<()> {
12831        // PostgreSQL ONLY modifier: prevents scanning child tables
12832        if table.only {
12833            self.write_keyword("ONLY");
12834            self.write_space();
12835        }
12836
12837        // Check for Snowflake IDENTIFIER() function
12838        if let Some(ref identifier_func) = table.identifier_func {
12839            self.generate_expression(identifier_func)?;
12840        } else {
12841            if let Some(catalog) = &table.catalog {
12842                self.generate_identifier(catalog)?;
12843                self.write(".");
12844            }
12845            if let Some(schema) = &table.schema {
12846                self.generate_identifier(schema)?;
12847                self.write(".");
12848            }
12849            self.generate_identifier(&table.name)?;
12850        }
12851
12852        // Output Snowflake CHANGES clause (before partition, includes its own AT/BEFORE/END)
12853        if let Some(changes) = &table.changes {
12854            self.write(" ");
12855            self.generate_changes(changes)?;
12856        }
12857
12858        // Output MySQL PARTITION clause: t1 PARTITION(p0, p1)
12859        if !table.partitions.is_empty() {
12860            self.write_space();
12861            self.write_keyword("PARTITION");
12862            self.write("(");
12863            for (i, partition) in table.partitions.iter().enumerate() {
12864                if i > 0 {
12865                    self.write(", ");
12866                }
12867                self.generate_identifier(partition)?;
12868            }
12869            self.write(")");
12870        }
12871
12872        // Output time travel clause: BEFORE (STATEMENT => ...) or AT (TIMESTAMP => ...)
12873        // Skip if CHANGES clause is present (CHANGES includes its own time travel)
12874        if table.changes.is_none() {
12875            if let Some(when) = &table.when {
12876                self.write_space();
12877                self.generate_historical_data(when)?;
12878            }
12879        }
12880
12881        // Output TSQL FOR SYSTEM_TIME temporal clause
12882        if let Some(ref system_time) = table.system_time {
12883            self.write_space();
12884            self.write(system_time);
12885        }
12886
12887        // Output Presto/Trino time travel: FOR VERSION AS OF / FOR TIMESTAMP AS OF
12888        if let Some(ref version) = table.version {
12889            self.write_space();
12890            self.generate_version(version)?;
12891        }
12892
12893        // When alias_post_tablesample is true, the order is: table TABLESAMPLE (...) alias
12894        // When alias_post_tablesample is false (default), the order is: table alias TABLESAMPLE (...)
12895        // Oracle, Hive, Spark use ALIAS_POST_TABLESAMPLE = true (alias comes after sample)
12896        let alias_post_tablesample = self.config.alias_post_tablesample;
12897
12898        if alias_post_tablesample {
12899            // TABLESAMPLE before alias (Oracle, Hive, Spark)
12900            self.generate_table_sample_clause(table)?;
12901        }
12902
12903        // Output table hints (TSQL: WITH (TABLOCK, INDEX(myindex), ...))
12904        // For SQLite, INDEXED BY hints come after the alias, so skip here
12905        let is_sqlite_hint = matches!(self.config.dialect, Some(DialectType::SQLite))
12906            && table.hints.iter().any(|h| {
12907                if let Expression::Identifier(id) = h {
12908                    id.name.starts_with("INDEXED BY") || id.name == "NOT INDEXED"
12909                } else {
12910                    false
12911                }
12912            });
12913        if !table.hints.is_empty() && !is_sqlite_hint {
12914            for hint in &table.hints {
12915                self.write_space();
12916                self.generate_expression(hint)?;
12917            }
12918        }
12919
12920        if let Some(alias) = &table.alias {
12921            self.write_space();
12922            // Output AS if it was explicitly present in the input, OR for certain dialects/cases
12923            // PostgreSQL/Redshift/Snowflake/BigQuery/Presto/Trino always use AS for table aliases
12924            let always_use_as = matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) | Some(DialectType::Snowflake) | Some(DialectType::BigQuery) | Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::TSQL) | Some(DialectType::Fabric) | Some(DialectType::MySQL) | Some(DialectType::Spark) | Some(DialectType::Hive));
12925            let is_stage_ref = table.name.name.starts_with('@');
12926            if table.alias_explicit_as || always_use_as || is_stage_ref {
12927                self.write_keyword("AS");
12928                self.write_space();
12929            }
12930            self.generate_identifier(alias)?;
12931
12932            // Output column aliases if present: AS t(c1, c2)
12933            // Skip for dialects that don't support table alias columns (BigQuery, SQLite)
12934            if !table.column_aliases.is_empty() && self.config.supports_table_alias_columns {
12935                self.write("(");
12936                for (i, col_alias) in table.column_aliases.iter().enumerate() {
12937                    if i > 0 {
12938                        self.write(", ");
12939                    }
12940                    self.generate_identifier(col_alias)?;
12941                }
12942                self.write(")");
12943            }
12944        }
12945
12946        // For default behavior (alias_post_tablesample = false), output TABLESAMPLE after alias
12947        if !alias_post_tablesample {
12948            self.generate_table_sample_clause(table)?;
12949        }
12950
12951        // Output SQLite INDEXED BY / NOT INDEXED hints after alias
12952        if is_sqlite_hint {
12953            for hint in &table.hints {
12954                self.write_space();
12955                self.generate_expression(hint)?;
12956            }
12957        }
12958
12959        // ClickHouse FINAL modifier
12960        if table.final_ && matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
12961            self.write_space();
12962            self.write_keyword("FINAL");
12963        }
12964
12965        // Output trailing comments
12966        for comment in &table.trailing_comments {
12967            self.write_space();
12968            self.write_formatted_comment(comment);
12969        }
12970
12971        Ok(())
12972    }
12973
12974    /// Helper to output TABLESAMPLE clause for a table reference
12975    fn generate_table_sample_clause(&mut self, table: &TableRef) -> Result<()> {
12976        if let Some(ref ts) = table.table_sample {
12977            self.write_space();
12978            if ts.is_using_sample {
12979                self.write_keyword("USING SAMPLE");
12980            } else {
12981                // Use the configured tablesample keyword (e.g., "TABLESAMPLE" or "SAMPLE")
12982                self.write_keyword(self.config.tablesample_keywords);
12983            }
12984            self.generate_sample_body(ts)?;
12985            // Seed for table-level sample - use dialect's configured keyword
12986            if let Some(ref seed) = ts.seed {
12987                self.write_space();
12988                self.write_keyword(self.config.tablesample_seed_keyword);
12989                self.write(" (");
12990                self.generate_expression(seed)?;
12991                self.write(")");
12992            }
12993        }
12994        Ok(())
12995    }
12996
12997    fn generate_stage_reference(&mut self, sr: &StageReference) -> Result<()> {
12998        // Output: '@stage_name/path' if quoted, or @stage_name/path otherwise
12999        // Optionally followed by (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
13000
13001        if sr.quoted {
13002            self.write("'");
13003        }
13004
13005        self.write(&sr.name);
13006        if let Some(path) = &sr.path {
13007            self.write(path);
13008        }
13009
13010        if sr.quoted {
13011            self.write("'");
13012        }
13013
13014        // Output FILE_FORMAT and PATTERN if present
13015        let has_options = sr.file_format.is_some() || sr.pattern.is_some();
13016        if has_options {
13017            self.write(" (");
13018            let mut first = true;
13019
13020            if let Some(file_format) = &sr.file_format {
13021                if !first {
13022                    self.write(", ");
13023                }
13024                self.write_keyword("FILE_FORMAT");
13025                self.write(" => ");
13026                self.generate_expression(file_format)?;
13027                first = false;
13028            }
13029
13030            if let Some(pattern) = &sr.pattern {
13031                if !first {
13032                    self.write(", ");
13033                }
13034                self.write_keyword("PATTERN");
13035                self.write(" => '");
13036                self.write(pattern);
13037                self.write("'");
13038            }
13039
13040            self.write(")");
13041        }
13042        Ok(())
13043    }
13044
13045
13046    fn generate_star(&mut self, star: &Star) -> Result<()> {
13047        use crate::dialects::DialectType;
13048
13049        if let Some(table) = &star.table {
13050            self.generate_identifier(table)?;
13051            self.write(".");
13052        }
13053        self.write("*");
13054
13055        // Generate EXCLUDE/EXCEPT clause based on dialect
13056        if let Some(except) = &star.except {
13057            if !except.is_empty() {
13058                self.write_space();
13059                // Use dialect-appropriate keyword
13060                match self.config.dialect {
13061                    Some(DialectType::BigQuery) => self.write_keyword("EXCEPT"),
13062                    Some(DialectType::DuckDB) | Some(DialectType::Snowflake) => {
13063                        self.write_keyword("EXCLUDE")
13064                    }
13065                    _ => self.write_keyword("EXCEPT"), // Default to EXCEPT
13066                }
13067                self.write(" (");
13068                for (i, col) in except.iter().enumerate() {
13069                    if i > 0 {
13070                        self.write(", ");
13071                    }
13072                    self.generate_identifier(col)?;
13073                }
13074                self.write(")");
13075            }
13076        }
13077
13078        // Generate REPLACE clause
13079        if let Some(replace) = &star.replace {
13080            if !replace.is_empty() {
13081                self.write_space();
13082                self.write_keyword("REPLACE");
13083                self.write(" (");
13084                for (i, alias) in replace.iter().enumerate() {
13085                    if i > 0 {
13086                        self.write(", ");
13087                    }
13088                    self.generate_expression(&alias.this)?;
13089                    self.write_space();
13090                    self.write_keyword("AS");
13091                    self.write_space();
13092                    self.generate_identifier(&alias.alias)?;
13093                }
13094                self.write(")");
13095            }
13096        }
13097
13098        // Generate RENAME clause (Snowflake specific)
13099        if let Some(rename) = &star.rename {
13100            if !rename.is_empty() {
13101                self.write_space();
13102                self.write_keyword("RENAME");
13103                self.write(" (");
13104                for (i, (old_name, new_name)) in rename.iter().enumerate() {
13105                    if i > 0 {
13106                        self.write(", ");
13107                    }
13108                    self.generate_identifier(old_name)?;
13109                    self.write_space();
13110                    self.write_keyword("AS");
13111                    self.write_space();
13112                    self.generate_identifier(new_name)?;
13113                }
13114                self.write(")");
13115            }
13116        }
13117
13118        // Output trailing comments
13119        for comment in &star.trailing_comments {
13120            self.write_space();
13121            self.write(comment);
13122        }
13123
13124        Ok(())
13125    }
13126
13127    /// Generate Snowflake braced wildcard syntax: {*}, {tbl.*}, {* EXCLUDE (...)}, {* ILIKE '...'}
13128    fn generate_braced_wildcard(&mut self, expr: &Expression) -> Result<()> {
13129        self.write("{");
13130        match expr {
13131            Expression::Star(star) => {
13132                // Generate the star (table.* or just * with optional EXCLUDE)
13133                self.generate_star(star)?;
13134            }
13135            Expression::ILike(ilike) => {
13136                // {* ILIKE 'pattern'} syntax
13137                self.generate_expression(&ilike.left)?;
13138                self.write_space();
13139                self.write_keyword("ILIKE");
13140                self.write_space();
13141                self.generate_expression(&ilike.right)?;
13142            }
13143            _ => {
13144                self.generate_expression(expr)?;
13145            }
13146        }
13147        self.write("}");
13148        Ok(())
13149    }
13150
13151    fn generate_alias(&mut self, alias: &Alias) -> Result<()> {
13152        // Generate inner expression, but skip trailing comments if they're in pre_alias_comments
13153        // to avoid duplication (comments are captured as both Column.trailing_comments
13154        // and Alias.pre_alias_comments during parsing)
13155        match &alias.this {
13156            Expression::Column(col) => {
13157                // Generate column without trailing comments - they're in pre_alias_comments
13158                if let Some(table) = &col.table {
13159                    self.generate_identifier(table)?;
13160                    self.write(".");
13161                }
13162                self.generate_identifier(&col.name)?;
13163            }
13164            _ => {
13165                self.generate_expression(&alias.this)?;
13166            }
13167        }
13168
13169        // Output pre-alias comments (comments between expression and AS)
13170        for comment in &alias.pre_alias_comments {
13171            self.write_space();
13172            self.write(comment);
13173        }
13174
13175        use crate::dialects::DialectType;
13176
13177        // Determine if we should skip AS keyword for table-valued function aliases
13178        // Oracle and some other dialects don't use AS for table aliases
13179        // Note: We specifically use TableFromRows here, NOT Function, because Function
13180        // matches regular functions like MATCH_NUMBER() which should include the AS keyword.
13181        // TableFromRows represents TABLE(expr) constructs which are actual table-valued functions.
13182        let is_table_source = matches!(
13183            &alias.this,
13184            Expression::JSONTable(_)
13185                | Expression::XMLTable(_)
13186                | Expression::TableFromRows(_)
13187                | Expression::Unnest(_)
13188                | Expression::MatchRecognize(_)
13189                | Expression::Select(_)
13190                | Expression::Subquery(_)
13191                | Expression::Paren(_)
13192        );
13193        let dialect_skips_table_alias_as = matches!(
13194            self.config.dialect,
13195            Some(DialectType::Oracle)
13196        );
13197        let skip_as = is_table_source && dialect_skips_table_alias_as;
13198
13199        self.write_space();
13200        if !skip_as {
13201            self.write_keyword("AS");
13202            self.write_space();
13203        }
13204
13205        // BigQuery doesn't support column aliases in table aliases: AS t(c1, c2)
13206        let skip_column_aliases = matches!(self.config.dialect, Some(DialectType::BigQuery));
13207
13208        // Check if we have column aliases only (no table alias name)
13209        if alias.alias.is_empty() && !alias.column_aliases.is_empty() && !skip_column_aliases {
13210            // Generate AS (col1, col2, ...)
13211            self.write("(");
13212            for (i, col_alias) in alias.column_aliases.iter().enumerate() {
13213                if i > 0 {
13214                    self.write(", ");
13215                }
13216                self.generate_alias_identifier(col_alias)?;
13217            }
13218            self.write(")");
13219        } else if !alias.column_aliases.is_empty() && !skip_column_aliases {
13220            // Generate AS alias(col1, col2, ...)
13221            self.generate_alias_identifier(&alias.alias)?;
13222            self.write("(");
13223            for (i, col_alias) in alias.column_aliases.iter().enumerate() {
13224                if i > 0 {
13225                    self.write(", ");
13226                }
13227                self.generate_alias_identifier(col_alias)?;
13228            }
13229            self.write(")");
13230        } else {
13231            // Simple alias (or BigQuery without column aliases)
13232            self.generate_alias_identifier(&alias.alias)?;
13233        }
13234
13235        // Output trailing comments (comments after the alias)
13236        for comment in &alias.trailing_comments {
13237            self.write_space();
13238            self.write(comment);
13239        }
13240
13241        Ok(())
13242    }
13243
13244    fn generate_cast(&mut self, cast: &Cast) -> Result<()> {
13245        use crate::dialects::DialectType;
13246
13247        // SingleStore uses :> syntax
13248        if matches!(self.config.dialect, Some(DialectType::SingleStore)) {
13249            self.generate_expression(&cast.this)?;
13250            self.write(" :> ");
13251            self.generate_data_type(&cast.to)?;
13252            return Ok(());
13253        }
13254
13255        // Teradata: CAST(x AS FORMAT 'fmt') (no data type)
13256        if matches!(self.config.dialect, Some(DialectType::Teradata)) {
13257            let is_unknown_type = matches!(cast.to, DataType::Unknown)
13258                || matches!(cast.to, DataType::Custom { ref name } if name.is_empty());
13259            if is_unknown_type {
13260                if let Some(format) = &cast.format {
13261                    self.write_keyword("CAST");
13262                    self.write("(");
13263                    self.generate_expression(&cast.this)?;
13264                    self.write_space();
13265                    self.write_keyword("AS");
13266                    self.write_space();
13267                    self.write_keyword("FORMAT");
13268                    self.write_space();
13269                    self.generate_expression(format)?;
13270                    self.write(")");
13271                    return Ok(());
13272                }
13273            }
13274        }
13275
13276        // Oracle: CAST(x AS DATE/TIMESTAMP ..., 'format') -> TO_DATE/TO_TIMESTAMP(x, 'format')
13277        // This follows Python sqlglot's behavior of transforming CAST with format to native functions
13278        if matches!(self.config.dialect, Some(DialectType::Oracle)) {
13279            if let Some(format) = &cast.format {
13280                // Check if target type is DATE or TIMESTAMP
13281                let is_date = matches!(cast.to, DataType::Date);
13282                let is_timestamp = matches!(cast.to, DataType::Timestamp { .. });
13283
13284                if is_date || is_timestamp {
13285                    let func_name = if is_date { "TO_DATE" } else { "TO_TIMESTAMP" };
13286                    self.write_keyword(func_name);
13287                    self.write("(");
13288                    self.generate_expression(&cast.this)?;
13289                    self.write(", ");
13290
13291                    // Normalize format string for Oracle (HH -> HH12)
13292                    // Oracle HH is 12-hour format, same as HH12. For clarity, Python sqlglot uses HH12.
13293                    if let Expression::Literal(Literal::String(fmt_str)) = format.as_ref() {
13294                        let normalized = self.normalize_oracle_format(fmt_str);
13295                        self.write("'");
13296                        self.write(&normalized);
13297                        self.write("'");
13298                    } else {
13299                        self.generate_expression(format)?;
13300                    }
13301
13302                    self.write(")");
13303                    return Ok(());
13304                }
13305            }
13306        }
13307
13308        // BigQuery: CAST(ARRAY[...] AS ARRAY<T>) -> ARRAY<T>[...]
13309        // This preserves sqlglot's typed inline array literal output.
13310        if matches!(self.config.dialect, Some(DialectType::BigQuery)) {
13311            if let Expression::Array(arr) = &cast.this {
13312                self.generate_data_type(&cast.to)?;
13313                // Output just the bracket content [values] without the ARRAY prefix
13314                self.write("[");
13315                for (i, expr) in arr.expressions.iter().enumerate() {
13316                    if i > 0 {
13317                        self.write(", ");
13318                    }
13319                    self.generate_expression(expr)?;
13320                }
13321                self.write("]");
13322                return Ok(());
13323            }
13324            if matches!(&cast.this, Expression::ArrayFunc(_)) {
13325                self.generate_data_type(&cast.to)?;
13326                self.generate_expression(&cast.this)?;
13327                return Ok(());
13328            }
13329        }
13330
13331        // DuckDB/Presto/Trino: When CAST(Struct([unnamed]) AS STRUCT(...)),
13332        // convert the inner Struct to ROW(values...) format
13333        if matches!(self.config.dialect, Some(DialectType::DuckDB) | Some(DialectType::Presto) | Some(DialectType::Trino)) {
13334            if let Expression::Struct(ref s) = cast.this {
13335                let all_unnamed = s.fields.iter().all(|(name, _)| name.is_none());
13336                if all_unnamed && matches!(cast.to, DataType::Struct { .. }) {
13337                    self.write_keyword("CAST");
13338                    self.write("(");
13339                    self.generate_struct_as_row(s)?;
13340                    self.write_space();
13341                    self.write_keyword("AS");
13342                    self.write_space();
13343                    self.generate_data_type(&cast.to)?;
13344                    self.write(")");
13345                    return Ok(());
13346                }
13347            }
13348        }
13349
13350        // Determine if we should use :: syntax based on dialect
13351        // PostgreSQL prefers :: for identity, most others prefer CAST()
13352        let use_double_colon = cast.double_colon_syntax && self.dialect_prefers_double_colon();
13353
13354        if use_double_colon {
13355            // PostgreSQL :: syntax: expr::type
13356            self.generate_expression(&cast.this)?;
13357            self.write("::");
13358            self.generate_data_type(&cast.to)?;
13359        } else {
13360            // Standard CAST() syntax
13361            self.write_keyword("CAST");
13362            self.write("(");
13363            self.generate_expression(&cast.this)?;
13364            self.write_space();
13365            self.write_keyword("AS");
13366            self.write_space();
13367            // For MySQL/SingleStore/TiDB, map text/blob variant Custom types to CHAR in CAST
13368            // This matches Python sqlglot's CHAR_CAST_MAPPING behavior
13369            if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB)) {
13370                if let DataType::Custom { ref name } = cast.to {
13371                    let upper = name.to_uppercase();
13372                    match upper.as_str() {
13373                        "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" | "LONGBLOB" | "MEDIUMBLOB" | "TINYBLOB" => {
13374                            self.write_keyword("CHAR");
13375                        }
13376                        _ => {
13377                            self.generate_data_type(&cast.to)?;
13378                        }
13379                    }
13380                } else {
13381                    self.generate_data_type(&cast.to)?;
13382                }
13383            } else {
13384                self.generate_data_type(&cast.to)?;
13385            }
13386
13387            // Output DEFAULT ... ON CONVERSION ERROR clause if present (Oracle)
13388            if let Some(default) = &cast.default {
13389                self.write_space();
13390                self.write_keyword("DEFAULT");
13391                self.write_space();
13392                self.generate_expression(default)?;
13393                self.write_space();
13394                self.write_keyword("ON");
13395                self.write_space();
13396                self.write_keyword("CONVERSION");
13397                self.write_space();
13398                self.write_keyword("ERROR");
13399            }
13400
13401            // Output FORMAT clause if present (BigQuery: CAST(x AS STRING FORMAT 'format'))
13402            // For Oracle with comma-separated format: CAST(x AS DATE DEFAULT NULL ON CONVERSION ERROR, 'format')
13403            if let Some(format) = &cast.format {
13404                // Check if Oracle dialect - use comma syntax
13405                if matches!(self.config.dialect, Some(crate::dialects::DialectType::Oracle)) {
13406                    self.write(", ");
13407                } else {
13408                    self.write_space();
13409                    self.write_keyword("FORMAT");
13410                    self.write_space();
13411                }
13412                self.generate_expression(format)?;
13413            }
13414
13415            self.write(")");
13416            // Output trailing comments
13417            for comment in &cast.trailing_comments {
13418                self.write_space();
13419                self.write(comment);
13420            }
13421        }
13422        Ok(())
13423    }
13424
13425    /// Generate a Struct as ROW(values...) format, recursively converting inner Struct to ROW too.
13426    /// Used for DuckDB/Presto/Trino CAST(Struct AS STRUCT(...)) context.
13427    fn generate_struct_as_row(&mut self, s: &crate::expressions::Struct) -> Result<()> {
13428        self.write_keyword("ROW");
13429        self.write("(");
13430        for (i, (_, expr)) in s.fields.iter().enumerate() {
13431            if i > 0 { self.write(", "); }
13432            // Recursively convert inner Struct to ROW format
13433            if let Expression::Struct(ref inner_s) = expr {
13434                self.generate_struct_as_row(inner_s)?;
13435            } else {
13436                self.generate_expression(expr)?;
13437            }
13438        }
13439        self.write(")");
13440        Ok(())
13441    }
13442
13443    /// Normalize Oracle date/time format strings
13444    /// HH -> HH12 (both are 12-hour format, but Python sqlglot prefers explicit HH12)
13445    fn normalize_oracle_format(&self, format: &str) -> String {
13446        // Replace standalone HH with HH12 (but not HH12 or HH24)
13447        // We need to be careful not to replace HH12 -> HH1212 or HH24 -> HH1224
13448        let mut result = String::new();
13449        let chars: Vec<char> = format.chars().collect();
13450        let mut i = 0;
13451
13452        while i < chars.len() {
13453            if i + 1 < chars.len() && chars[i] == 'H' && chars[i + 1] == 'H' {
13454                // Check what follows HH
13455                if i + 2 < chars.len() {
13456                    let next = chars[i + 2];
13457                    if next == '1' || next == '2' {
13458                        // This is HH12 or HH24, keep as is
13459                        result.push('H');
13460                        result.push('H');
13461                        i += 2;
13462                        continue;
13463                    }
13464                }
13465                // Standalone HH -> HH12
13466                result.push_str("HH12");
13467                i += 2;
13468            } else {
13469                result.push(chars[i]);
13470                i += 1;
13471            }
13472        }
13473
13474        result
13475    }
13476
13477    /// Check if the current dialect prefers :: cast syntax
13478    /// Note: Python sqlglot normalizes all :: to CAST() for output, even for PostgreSQL
13479    /// So we return false for all dialects to match Python sqlglot's behavior
13480    fn dialect_prefers_double_colon(&self) -> bool {
13481        // Python sqlglot normalizes :: syntax to CAST() for all dialects
13482        // Even PostgreSQL outputs CAST() not ::
13483        false
13484    }
13485
13486    /// Generate MOD function - uses % operator for Snowflake/MySQL/Presto/Trino, MOD() for others
13487    fn generate_mod_func(&mut self, f: &crate::expressions::BinaryFunc) -> Result<()> {
13488        use crate::dialects::DialectType;
13489
13490        // Snowflake, MySQL, Presto, Trino, PostgreSQL, and DuckDB prefer x % y instead of MOD(x, y)
13491        let use_percent_operator = matches!(
13492            self.config.dialect,
13493            Some(DialectType::Snowflake) | Some(DialectType::MySQL) | Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::PostgreSQL) | Some(DialectType::DuckDB)
13494        );
13495
13496        if use_percent_operator {
13497            self.generate_expression(&f.this)?;
13498            self.write(" % ");
13499            self.generate_expression(&f.expression)?;
13500            Ok(())
13501        } else {
13502            self.generate_binary_func("MOD", &f.this, &f.expression)
13503        }
13504    }
13505
13506    /// Generate IFNULL - uses COALESCE for Snowflake, IFNULL for others
13507    fn generate_ifnull(&mut self, f: &crate::expressions::BinaryFunc) -> Result<()> {
13508        use crate::dialects::DialectType;
13509
13510        // Snowflake normalizes IFNULL to COALESCE
13511        let func_name = match self.config.dialect {
13512            Some(DialectType::Snowflake) => "COALESCE",
13513            _ => "IFNULL",
13514        };
13515
13516        self.generate_binary_func(func_name, &f.this, &f.expression)
13517    }
13518
13519    /// Generate NVL - preserves original name if available, otherwise uses dialect-specific output
13520    fn generate_nvl(&mut self, f: &crate::expressions::BinaryFunc) -> Result<()> {
13521        // Use original function name if preserved (for identity tests)
13522        if let Some(ref original_name) = f.original_name {
13523            return self.generate_binary_func(original_name, &f.this, &f.expression);
13524        }
13525
13526        // Otherwise, use dialect-specific function names
13527        use crate::dialects::DialectType;
13528        let func_name = match self.config.dialect {
13529            Some(DialectType::Snowflake) |
13530            Some(DialectType::ClickHouse) |
13531            Some(DialectType::PostgreSQL) |
13532            Some(DialectType::Presto) |
13533            Some(DialectType::Trino) |
13534            Some(DialectType::Athena) |
13535            Some(DialectType::DuckDB) |
13536            Some(DialectType::BigQuery) |
13537            Some(DialectType::Spark) |
13538            Some(DialectType::Databricks) |
13539            Some(DialectType::Hive) => "COALESCE",
13540            Some(DialectType::MySQL) |
13541            Some(DialectType::Doris) |
13542            Some(DialectType::StarRocks) |
13543            Some(DialectType::SingleStore) |
13544            Some(DialectType::TiDB) => "IFNULL",
13545            _ => "NVL",
13546        };
13547
13548        self.generate_binary_func(func_name, &f.this, &f.expression)
13549    }
13550
13551    /// Generate STDDEV_SAMP - uses STDDEV for Snowflake, STDDEV_SAMP for others
13552    fn generate_stddev_samp(&mut self, f: &crate::expressions::AggFunc) -> Result<()> {
13553        use crate::dialects::DialectType;
13554
13555        // Snowflake normalizes STDDEV_SAMP to STDDEV
13556        let func_name = match self.config.dialect {
13557            Some(DialectType::Snowflake) => "STDDEV",
13558            _ => "STDDEV_SAMP",
13559        };
13560
13561        self.generate_agg_func(func_name, f)
13562    }
13563
13564    fn generate_collation(&mut self, coll: &CollationExpr) -> Result<()> {
13565        self.generate_expression(&coll.this)?;
13566        self.write_space();
13567        self.write_keyword("COLLATE");
13568        self.write_space();
13569        if coll.quoted {
13570            // Single-quoted string: COLLATE 'de_DE'
13571            self.write("'");
13572            self.write(&coll.collation);
13573            self.write("'");
13574        } else if coll.double_quoted {
13575            // Double-quoted identifier: COLLATE "de_DE"
13576            self.write("\"");
13577            self.write(&coll.collation);
13578            self.write("\"");
13579        } else {
13580            // Unquoted identifier: COLLATE de_DE
13581            self.write(&coll.collation);
13582        }
13583        Ok(())
13584    }
13585
13586    fn generate_case(&mut self, case: &Case) -> Result<()> {
13587        let multiline_case = self.config.pretty
13588            && matches!(self.config.dialect, Some(DialectType::Snowflake));
13589
13590        self.write_keyword("CASE");
13591        if let Some(operand) = &case.operand {
13592            self.write_space();
13593            self.generate_expression(operand)?;
13594        }
13595        if multiline_case {
13596            self.indent_level += 1;
13597        }
13598        for (condition, result) in &case.whens {
13599            if multiline_case {
13600                self.write_newline();
13601                self.write_indent();
13602            } else {
13603                self.write_space();
13604            }
13605            self.write_keyword("WHEN");
13606            self.write_space();
13607            self.generate_expression(condition)?;
13608            if multiline_case {
13609                self.write_newline();
13610                self.write_indent();
13611            } else {
13612                self.write_space();
13613            }
13614            self.write_keyword("THEN");
13615            self.write_space();
13616            self.generate_expression(result)?;
13617        }
13618        if let Some(else_) = &case.else_ {
13619            if multiline_case {
13620                self.write_newline();
13621                self.write_indent();
13622            } else {
13623                self.write_space();
13624            }
13625            self.write_keyword("ELSE");
13626            self.write_space();
13627            self.generate_expression(else_)?;
13628        }
13629        if multiline_case {
13630            self.indent_level -= 1;
13631            self.write_newline();
13632            self.write_indent();
13633        } else {
13634            self.write_space();
13635        }
13636        self.write_keyword("END");
13637        Ok(())
13638    }
13639
13640    fn generate_function(&mut self, func: &Function) -> Result<()> {
13641        // Normalize function name based on dialect settings
13642        let normalized_name = self.normalize_func_name(&func.name);
13643        let upper_name = func.name.to_uppercase();
13644
13645        // STRUCT function: BigQuery STRUCT('Alice' AS name, 85 AS score) -> dialect-specific
13646        if upper_name == "STRUCT" && !matches!(self.config.dialect, Some(DialectType::BigQuery) | Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) | None) {
13647            return self.generate_struct_function_cross_dialect(func);
13648        }
13649
13650        // SingleStore: __SS_JSON_PATH_QMARK__(expr, key) -> expr::?key
13651        // This is an internal marker function for ::? JSON path syntax
13652        if upper_name == "__SS_JSON_PATH_QMARK__" && func.args.len() == 2 {
13653            self.generate_expression(&func.args[0])?;
13654            self.write("::?");
13655            // Extract the key from the string literal
13656            if let Expression::Literal(crate::expressions::Literal::String(key)) = &func.args[1] {
13657                self.write(key);
13658            } else {
13659                self.generate_expression(&func.args[1])?;
13660            }
13661            return Ok(());
13662        }
13663
13664        // PostgreSQL: __PG_BITWISE_XOR__(a, b) -> a # b
13665        if upper_name == "__PG_BITWISE_XOR__" && func.args.len() == 2 {
13666            self.generate_expression(&func.args[0])?;
13667            self.write(" # ");
13668            self.generate_expression(&func.args[1])?;
13669            return Ok(());
13670        }
13671
13672        // Spark/Hive family: unwrap TRY(expr) since these dialects don't emit TRY as a scalar wrapper.
13673        if matches!(
13674            self.config.dialect,
13675            Some(DialectType::Spark | DialectType::Databricks | DialectType::Hive)
13676        ) && upper_name == "TRY"
13677            && func.args.len() == 1
13678        {
13679            self.generate_expression(&func.args[0])?;
13680            return Ok(());
13681        }
13682
13683        // ClickHouse normalization: toStartOfDay(x) -> dateTrunc('DAY', x)
13684        if self.config.dialect == Some(DialectType::ClickHouse)
13685            && upper_name == "TOSTARTOFDAY"
13686            && func.args.len() == 1
13687        {
13688            self.write("dateTrunc('DAY', ");
13689            self.generate_expression(&func.args[0])?;
13690            self.write(")");
13691            return Ok(());
13692        }
13693
13694        // Redshift: CONCAT(a, b, ...) -> a || b || ...
13695        if self.config.dialect == Some(DialectType::Redshift) && upper_name == "CONCAT" && func.args.len() >= 2 {
13696            for (i, arg) in func.args.iter().enumerate() {
13697                if i > 0 {
13698                    self.write(" || ");
13699                }
13700                self.generate_expression(arg)?;
13701            }
13702            return Ok(());
13703        }
13704
13705        // Redshift: CONCAT_WS(delim, a, b, c) -> a || delim || b || delim || c
13706        if self.config.dialect == Some(DialectType::Redshift) && upper_name == "CONCAT_WS" && func.args.len() >= 2 {
13707            let sep = &func.args[0];
13708            for (i, arg) in func.args.iter().skip(1).enumerate() {
13709                if i > 0 {
13710                    self.write(" || ");
13711                    self.generate_expression(sep)?;
13712                    self.write(" || ");
13713                }
13714                self.generate_expression(arg)?;
13715            }
13716            return Ok(());
13717        }
13718
13719        // Redshift: DATEDIFF/DATE_DIFF(unit, start, end) -> DATEDIFF(UNIT, start, end)
13720        // Unit should be unquoted uppercase identifier
13721        if self.config.dialect == Some(DialectType::Redshift)
13722            && (upper_name == "DATEDIFF" || upper_name == "DATE_DIFF")
13723            && func.args.len() == 3
13724        {
13725            self.write_keyword("DATEDIFF");
13726            self.write("(");
13727            // First arg is unit - normalize to unquoted uppercase
13728            self.write_redshift_date_part(&func.args[0]);
13729            self.write(", ");
13730            self.generate_expression(&func.args[1])?;
13731            self.write(", ");
13732            self.generate_expression(&func.args[2])?;
13733            self.write(")");
13734            return Ok(());
13735        }
13736
13737        // Redshift: DATEADD/DATE_ADD(unit, interval, date) -> DATEADD(UNIT, interval, date)
13738        // Unit should be unquoted uppercase identifier
13739        if self.config.dialect == Some(DialectType::Redshift)
13740            && (upper_name == "DATEADD" || upper_name == "DATE_ADD")
13741            && func.args.len() == 3
13742        {
13743            self.write_keyword("DATEADD");
13744            self.write("(");
13745            // First arg is unit - normalize to unquoted uppercase
13746            self.write_redshift_date_part(&func.args[0]);
13747            self.write(", ");
13748            self.generate_expression(&func.args[1])?;
13749            self.write(", ");
13750            self.generate_expression(&func.args[2])?;
13751            self.write(")");
13752            return Ok(());
13753        }
13754
13755        // UUID_STRING(args) from Snowflake -> dialect-specific UUID function (dropping args)
13756        if upper_name == "UUID_STRING" && !matches!(self.config.dialect, Some(DialectType::Snowflake) | None) {
13757            let func_name = match self.config.dialect {
13758                Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => "GEN_RANDOM_UUID",
13759                Some(DialectType::BigQuery) => "GENERATE_UUID",
13760                _ => "UUID",
13761            };
13762            self.write_keyword(func_name);
13763            self.write("()");
13764            return Ok(());
13765        }
13766
13767        // Redshift: DATE_TRUNC('unit', date) -> DATE_TRUNC('UNIT', date)
13768        // Unit should be quoted uppercase string
13769        if self.config.dialect == Some(DialectType::Redshift)
13770            && upper_name == "DATE_TRUNC"
13771            && func.args.len() == 2
13772        {
13773            self.write_keyword("DATE_TRUNC");
13774            self.write("(");
13775            // First arg is unit - normalize to quoted uppercase
13776            self.write_redshift_date_part_quoted(&func.args[0]);
13777            self.write(", ");
13778            self.generate_expression(&func.args[1])?;
13779            self.write(")");
13780            return Ok(());
13781        }
13782
13783        // TSQL/Fabric: DATE_PART -> DATEPART (no underscore)
13784        if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric))
13785            && (upper_name == "DATE_PART" || upper_name == "DATEPART")
13786            && func.args.len() == 2
13787        {
13788            self.write_keyword("DATEPART");
13789            self.write("(");
13790            self.generate_expression(&func.args[0])?;
13791            self.write(", ");
13792            self.generate_expression(&func.args[1])?;
13793            self.write(")");
13794            return Ok(());
13795        }
13796
13797        // PostgreSQL/Redshift: DATE_PART(part, value) -> EXTRACT(part FROM value)
13798        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift))
13799            && (upper_name == "DATE_PART" || upper_name == "DATEPART")
13800            && func.args.len() == 2
13801        {
13802            self.write_keyword("EXTRACT");
13803            self.write("(");
13804            // Extract the datetime field - if it's a string literal, strip quotes to make it a keyword
13805            match &func.args[0] {
13806                Expression::Literal(crate::expressions::Literal::String(s)) => {
13807                    self.write(&s.to_lowercase());
13808                }
13809                _ => self.generate_expression(&func.args[0])?,
13810            }
13811            self.write_space();
13812            self.write_keyword("FROM");
13813            self.write_space();
13814            self.generate_expression(&func.args[1])?;
13815            self.write(")");
13816            return Ok(());
13817        }
13818
13819        // Dremio: DATE_PART(part, value) -> EXTRACT(part FROM value)
13820        // Also DATE literals in Dremio should be CAST(...AS DATE)
13821        if self.config.dialect == Some(DialectType::Dremio)
13822            && (upper_name == "DATE_PART" || upper_name == "DATEPART")
13823            && func.args.len() == 2
13824        {
13825            self.write_keyword("EXTRACT");
13826            self.write("(");
13827            self.generate_expression(&func.args[0])?;
13828            self.write_space();
13829            self.write_keyword("FROM");
13830            self.write_space();
13831            // For Dremio, DATE literals should become CAST('value' AS DATE)
13832            self.generate_dremio_date_expression(&func.args[1])?;
13833            self.write(")");
13834            return Ok(());
13835        }
13836
13837        // Dremio: CURRENT_DATE_UTC() -> CURRENT_DATE_UTC (no parentheses)
13838        if self.config.dialect == Some(DialectType::Dremio)
13839            && upper_name == "CURRENT_DATE_UTC"
13840            && func.args.is_empty()
13841        {
13842            self.write_keyword("CURRENT_DATE_UTC");
13843            return Ok(());
13844        }
13845
13846        // Dremio: DATETYPE(year, month, day) transformation
13847        // - If all args are integer literals: DATE('YYYY-MM-DD')
13848        // - If args are expressions: CAST(CONCAT(x, '-', y, '-', z) AS DATE)
13849        if self.config.dialect == Some(DialectType::Dremio)
13850            && upper_name == "DATETYPE"
13851            && func.args.len() == 3
13852        {
13853            // Helper function to extract integer from number literal
13854            fn get_int_literal(expr: &Expression) -> Option<i64> {
13855                if let Expression::Literal(crate::expressions::Literal::Number(s)) = expr {
13856                    s.parse::<i64>().ok()
13857                } else {
13858                    None
13859                }
13860            }
13861
13862            // Check if all arguments are integer literals
13863            if let (Some(year), Some(month), Some(day)) = (
13864                get_int_literal(&func.args[0]),
13865                get_int_literal(&func.args[1]),
13866                get_int_literal(&func.args[2]),
13867            ) {
13868                // All are integer literals: DATE('YYYY-MM-DD')
13869                self.write_keyword("DATE");
13870                self.write(&format!("('{:04}-{:02}-{:02}')", year, month, day));
13871                return Ok(());
13872            }
13873
13874            // For expressions: CAST(CONCAT(x, '-', y, '-', z) AS DATE)
13875            self.write_keyword("CAST");
13876            self.write("(");
13877            self.write_keyword("CONCAT");
13878            self.write("(");
13879            self.generate_expression(&func.args[0])?;
13880            self.write(", '-', ");
13881            self.generate_expression(&func.args[1])?;
13882            self.write(", '-', ");
13883            self.generate_expression(&func.args[2])?;
13884            self.write(")");
13885            self.write_space();
13886            self.write_keyword("AS");
13887            self.write_space();
13888            self.write_keyword("DATE");
13889            self.write(")");
13890            return Ok(());
13891        }
13892
13893        // Presto/Trino: DATE_ADD('unit', interval, date) - wrap interval in CAST(...AS BIGINT)
13894        // when it's not an integer literal
13895        let is_presto_like = matches!(
13896            self.config.dialect,
13897            Some(DialectType::Presto) | Some(DialectType::Trino)
13898        );
13899        if is_presto_like
13900            && upper_name == "DATE_ADD"
13901            && func.args.len() == 3
13902        {
13903            self.write_keyword("DATE_ADD");
13904            self.write("(");
13905            // First arg: unit (pass through as-is, e.g., 'DAY')
13906            self.generate_expression(&func.args[0])?;
13907            self.write(", ");
13908            // Second arg: interval - wrap in CAST(...AS BIGINT) if it doesn't return integer type
13909            let interval = &func.args[1];
13910            let needs_cast = !self.returns_integer_type(interval);
13911            if needs_cast {
13912                self.write_keyword("CAST");
13913                self.write("(");
13914            }
13915            self.generate_expression(interval)?;
13916            if needs_cast {
13917                self.write_space();
13918                self.write_keyword("AS");
13919                self.write_space();
13920                self.write_keyword("BIGINT");
13921                self.write(")");
13922            }
13923            self.write(", ");
13924            // Third arg: date
13925            self.generate_expression(&func.args[2])?;
13926            self.write(")");
13927            return Ok(());
13928        }
13929
13930        // Use bracket syntax if the function was parsed with brackets (e.g., MAP[keys, values])
13931        let use_brackets = func.use_bracket_syntax;
13932
13933        // Special case: functions WITH ORDINALITY need special output order
13934        // Input: FUNC(args) WITH ORDINALITY
13935        // Stored as: name="FUNC WITH ORDINALITY", args=[...]
13936        // Output must be: FUNC(args) WITH ORDINALITY
13937        let has_ordinality = upper_name.ends_with(" WITH ORDINALITY");
13938        let output_name = if has_ordinality {
13939            let base_name = &func.name[..func.name.len() - " WITH ORDINALITY".len()];
13940            self.normalize_func_name(base_name)
13941        } else {
13942            normalized_name.clone()
13943        };
13944
13945        // For qualified names (schema.function or object.method), preserve original case
13946        // because they can be case-sensitive (e.g., TSQL XML methods like .nodes(), .value())
13947        if func.name.contains('.') && !has_ordinality {
13948            // Don't normalize qualified functions - preserve original case
13949            // If the function was quoted (e.g., BigQuery `p.d.UdF`), wrap it in backticks
13950            if func.quoted {
13951                self.write("`");
13952                self.write(&func.name);
13953                self.write("`");
13954            } else {
13955                self.write(&func.name);
13956            }
13957        } else {
13958            self.write(&output_name);
13959        }
13960
13961        // If no_parens is true and there are no args, output just the function name
13962        // Unless the target dialect requires parens for this function
13963        let force_parens = func.no_parens && func.args.is_empty() && !func.distinct && {
13964            let needs_parens = match upper_name.as_str() {
13965                "CURRENT_USER" | "SESSION_USER" | "SYSTEM_USER" => matches!(
13966                    self.config.dialect,
13967                    Some(DialectType::Snowflake) | Some(DialectType::Spark)
13968                    | Some(DialectType::Databricks) | Some(DialectType::Hive)
13969                ),
13970                _ => false,
13971            };
13972            !needs_parens
13973        };
13974        if force_parens {
13975            // Output trailing comments
13976            for comment in &func.trailing_comments {
13977                self.write_space();
13978                self.write(comment);
13979            }
13980            return Ok(());
13981        }
13982
13983        // CUBE, ROLLUP, GROUPING SETS need a space before the parenthesis
13984        if upper_name == "CUBE" || upper_name == "ROLLUP" || upper_name == "GROUPING SETS" {
13985            self.write(" (");
13986        } else if use_brackets {
13987            self.write("[");
13988        } else {
13989            self.write("(");
13990        }
13991        if func.distinct {
13992            self.write_keyword("DISTINCT");
13993            self.write_space();
13994        }
13995
13996        // Check if arguments should be split onto multiple lines (pretty + too wide)
13997        let compact_pretty_func = matches!(self.config.dialect, Some(DialectType::Snowflake))
13998            && (upper_name == "TABLE" || upper_name == "FLATTEN");
13999        let should_split = if self.config.pretty && !func.args.is_empty() && !compact_pretty_func {
14000            // Pre-render arguments to check total width
14001            let mut expr_strings: Vec<String> = Vec::with_capacity(func.args.len());
14002            for arg in &func.args {
14003                let mut temp_gen = Generator::with_config(self.config.clone());
14004                temp_gen.config.pretty = false; // Don't recurse into pretty
14005                temp_gen.generate_expression(arg)?;
14006                expr_strings.push(temp_gen.output);
14007            }
14008            self.too_wide(&expr_strings)
14009        } else {
14010            false
14011        };
14012
14013        if should_split {
14014            // Split onto multiple lines
14015            self.write_newline();
14016            self.indent_level += 1;
14017            for (i, arg) in func.args.iter().enumerate() {
14018                self.write_indent();
14019                self.generate_expression(arg)?;
14020                if i + 1 < func.args.len() {
14021                    self.write(",");
14022                }
14023                self.write_newline();
14024            }
14025            self.indent_level -= 1;
14026            self.write_indent();
14027        } else {
14028            // All on one line
14029            for (i, arg) in func.args.iter().enumerate() {
14030                if i > 0 {
14031                    self.write(", ");
14032                }
14033                self.generate_expression(arg)?;
14034            }
14035        }
14036
14037        if use_brackets {
14038            self.write("]");
14039        } else {
14040            self.write(")");
14041        }
14042        // Append WITH ORDINALITY after closing paren for table-valued functions
14043        if has_ordinality {
14044            self.write_space();
14045            self.write_keyword("WITH ORDINALITY");
14046        }
14047        // Output trailing comments
14048        for comment in &func.trailing_comments {
14049            self.write_space();
14050            self.write(comment);
14051        }
14052        Ok(())
14053    }
14054
14055    fn generate_aggregate_function(&mut self, func: &AggregateFunction) -> Result<()> {
14056        // Normalize function name based on dialect settings
14057        let mut normalized_name = self.normalize_func_name(&func.name);
14058
14059        // Dialect-specific name mappings for aggregate functions
14060        let upper = normalized_name.to_uppercase();
14061        if upper == "MAX_BY" || upper == "MIN_BY" {
14062            let is_max = upper == "MAX_BY";
14063            match self.config.dialect {
14064                Some(DialectType::ClickHouse) => {
14065                    normalized_name = if is_max { "argMax".to_string() } else { "argMin".to_string() };
14066                }
14067                Some(DialectType::DuckDB) => {
14068                    normalized_name = if is_max { "ARG_MAX".to_string() } else { "ARG_MIN".to_string() };
14069                }
14070                _ => {}
14071            }
14072        }
14073        self.write(&normalized_name);
14074        self.write("(");
14075        if func.distinct {
14076            self.write_keyword("DISTINCT");
14077            self.write_space();
14078        }
14079
14080        // Check if we need to transform multi-arg COUNT DISTINCT
14081        // When dialect doesn't support multi_arg_distinct, transform:
14082        // COUNT(DISTINCT a, b) -> COUNT(DISTINCT CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE (a, b) END)
14083        let is_count = normalized_name.eq_ignore_ascii_case("COUNT");
14084        let needs_multi_arg_transform = func.distinct
14085            && is_count
14086            && func.args.len() > 1
14087            && !self.config.multi_arg_distinct;
14088
14089        if needs_multi_arg_transform {
14090            // Generate: CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE (a, b) END
14091            self.write_keyword("CASE");
14092            for arg in &func.args {
14093                self.write_space();
14094                self.write_keyword("WHEN");
14095                self.write_space();
14096                self.generate_expression(arg)?;
14097                self.write_space();
14098                self.write_keyword("IS NULL THEN NULL");
14099            }
14100            self.write_space();
14101            self.write_keyword("ELSE");
14102            self.write(" (");
14103            for (i, arg) in func.args.iter().enumerate() {
14104                if i > 0 {
14105                    self.write(", ");
14106                }
14107                self.generate_expression(arg)?;
14108            }
14109            self.write(")");
14110            self.write_space();
14111            self.write_keyword("END");
14112        } else {
14113            for (i, arg) in func.args.iter().enumerate() {
14114                if i > 0 {
14115                    self.write(", ");
14116                }
14117                self.generate_expression(arg)?;
14118            }
14119        }
14120
14121        // IGNORE NULLS / RESPECT NULLS inside parens (for BigQuery style or when config says in_func)
14122        if self.config.ignore_nulls_in_func && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
14123            if let Some(ignore) = func.ignore_nulls {
14124                self.write_space();
14125                if ignore {
14126                    self.write_keyword("IGNORE NULLS");
14127                } else {
14128                    self.write_keyword("RESPECT NULLS");
14129                }
14130            }
14131        }
14132
14133        // ORDER BY inside aggregate
14134        if !func.order_by.is_empty() {
14135            self.write_space();
14136            self.write_keyword("ORDER BY");
14137            self.write_space();
14138            for (i, ord) in func.order_by.iter().enumerate() {
14139                if i > 0 {
14140                    self.write(", ");
14141                }
14142                self.generate_ordered(ord)?;
14143            }
14144        }
14145
14146        // LIMIT inside aggregate
14147        if let Some(limit) = &func.limit {
14148            self.write_space();
14149            self.write_keyword("LIMIT");
14150            self.write_space();
14151            // Check if this is a Tuple representing LIMIT offset, count
14152            if let Expression::Tuple(t) = limit.as_ref() {
14153                if t.expressions.len() == 2 {
14154                    self.generate_expression(&t.expressions[0])?;
14155                    self.write(", ");
14156                    self.generate_expression(&t.expressions[1])?;
14157                } else {
14158                    self.generate_expression(limit)?;
14159                }
14160            } else {
14161                self.generate_expression(limit)?;
14162            }
14163        }
14164
14165        self.write(")");
14166
14167        // IGNORE NULLS / RESPECT NULLS outside parens (standard style)
14168        if !self.config.ignore_nulls_in_func && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
14169            if let Some(ignore) = func.ignore_nulls {
14170                self.write_space();
14171                if ignore {
14172                    self.write_keyword("IGNORE NULLS");
14173                } else {
14174                    self.write_keyword("RESPECT NULLS");
14175                }
14176            }
14177        }
14178
14179        if let Some(filter) = &func.filter {
14180            self.write_space();
14181            self.write_keyword("FILTER");
14182            self.write("(");
14183            self.write_keyword("WHERE");
14184            self.write_space();
14185            self.generate_expression(filter)?;
14186            self.write(")");
14187        }
14188
14189        Ok(())
14190    }
14191
14192    fn generate_window_function(&mut self, wf: &WindowFunction) -> Result<()> {
14193        self.generate_expression(&wf.this)?;
14194
14195        // Generate KEEP clause if present (Oracle KEEP (DENSE_RANK FIRST|LAST ORDER BY ...))
14196        if let Some(keep) = &wf.keep {
14197            self.write_space();
14198            self.write_keyword("KEEP");
14199            self.write(" (");
14200            self.write_keyword("DENSE_RANK");
14201            self.write_space();
14202            if keep.first {
14203                self.write_keyword("FIRST");
14204            } else {
14205                self.write_keyword("LAST");
14206            }
14207            self.write_space();
14208            self.write_keyword("ORDER BY");
14209            self.write_space();
14210            for (i, ord) in keep.order_by.iter().enumerate() {
14211                if i > 0 {
14212                    self.write(", ");
14213                }
14214                self.generate_ordered(ord)?;
14215            }
14216            self.write(")");
14217        }
14218
14219        // Check if there's any OVER clause content
14220        let has_over = !wf.over.partition_by.is_empty()
14221            || !wf.over.order_by.is_empty()
14222            || wf.over.frame.is_some()
14223            || wf.over.window_name.is_some();
14224
14225        // Only output OVER if there's actual window specification (not just KEEP alone)
14226        if has_over {
14227            self.write_space();
14228            self.write_keyword("OVER");
14229
14230            // Check if this is just a bare named window reference (no parens needed)
14231            let has_specs = !wf.over.partition_by.is_empty()
14232                || !wf.over.order_by.is_empty()
14233                || wf.over.frame.is_some();
14234
14235            if wf.over.window_name.is_some() && !has_specs {
14236                // OVER window_name (without parentheses)
14237                self.write_space();
14238                self.write(&wf.over.window_name.as_ref().unwrap().name);
14239            } else {
14240                // OVER (...) or OVER (window_name ...)
14241                self.write(" (");
14242                self.generate_over(&wf.over)?;
14243                self.write(")");
14244            }
14245        } else if wf.keep.is_none() {
14246            // No KEEP and no OVER content, but still a WindowFunction - output empty OVER ()
14247            self.write_space();
14248            self.write_keyword("OVER");
14249            self.write(" ()");
14250        }
14251
14252        Ok(())
14253    }
14254
14255    /// Generate WITHIN GROUP clause (for ordered-set aggregate functions)
14256    fn generate_within_group(&mut self, wg: &WithinGroup) -> Result<()> {
14257        self.generate_expression(&wg.this)?;
14258        self.write_space();
14259        self.write_keyword("WITHIN GROUP");
14260        self.write(" (");
14261        self.write_keyword("ORDER BY");
14262        self.write_space();
14263        for (i, ord) in wg.order_by.iter().enumerate() {
14264            if i > 0 {
14265                self.write(", ");
14266            }
14267            self.generate_ordered(ord)?;
14268        }
14269        self.write(")");
14270        Ok(())
14271    }
14272
14273    /// Generate the contents of an OVER clause (without parentheses)
14274    fn generate_over(&mut self, over: &Over) -> Result<()> {
14275        let mut has_content = false;
14276
14277        // Named window reference
14278        if let Some(name) = &over.window_name {
14279            self.write(&name.name);
14280            has_content = true;
14281        }
14282
14283        // PARTITION BY
14284        if !over.partition_by.is_empty() {
14285            if has_content {
14286                self.write_space();
14287            }
14288            self.write_keyword("PARTITION BY");
14289            self.write_space();
14290            for (i, expr) in over.partition_by.iter().enumerate() {
14291                if i > 0 {
14292                    self.write(", ");
14293                }
14294                self.generate_expression(expr)?;
14295            }
14296            has_content = true;
14297        }
14298
14299        // ORDER BY
14300        if !over.order_by.is_empty() {
14301            if has_content {
14302                self.write_space();
14303            }
14304            self.write_keyword("ORDER BY");
14305            self.write_space();
14306            for (i, ordered) in over.order_by.iter().enumerate() {
14307                if i > 0 {
14308                    self.write(", ");
14309                }
14310                self.generate_ordered(ordered)?;
14311            }
14312            has_content = true;
14313        }
14314
14315        // Window frame
14316        if let Some(frame) = &over.frame {
14317            if has_content {
14318                self.write_space();
14319            }
14320            self.generate_window_frame(frame)?;
14321        }
14322
14323        Ok(())
14324    }
14325
14326    fn generate_window_frame(&mut self, frame: &WindowFrame) -> Result<()> {
14327        // Exasol uses lowercase for frame kind (rows/range/groups)
14328        let lowercase_frame = self.config.lowercase_window_frame_keywords;
14329
14330        // Use preserved kind_text if available (for case preservation), unless lowercase override is active
14331        if !lowercase_frame {
14332            if let Some(kind_text) = &frame.kind_text {
14333                self.write(kind_text);
14334            } else {
14335                match frame.kind {
14336                    WindowFrameKind::Rows => self.write_keyword("ROWS"),
14337                    WindowFrameKind::Range => self.write_keyword("RANGE"),
14338                    WindowFrameKind::Groups => self.write_keyword("GROUPS"),
14339                }
14340            }
14341        } else {
14342            match frame.kind {
14343                WindowFrameKind::Rows => self.write("rows"),
14344                WindowFrameKind::Range => self.write("range"),
14345                WindowFrameKind::Groups => self.write("groups"),
14346            }
14347        }
14348
14349        // Use BETWEEN format only when there's an explicit end bound,
14350        // or when normalize_window_frame_between is enabled and the start is a directional bound
14351        self.write_space();
14352        let should_normalize = self.config.normalize_window_frame_between
14353            && frame.end.is_none()
14354            && matches!(
14355                frame.start,
14356                WindowFrameBound::Preceding(_)
14357                    | WindowFrameBound::Following(_)
14358                    | WindowFrameBound::UnboundedPreceding
14359                    | WindowFrameBound::UnboundedFollowing
14360            );
14361
14362        if let Some(end) = &frame.end {
14363            // BETWEEN format: RANGE BETWEEN start AND end
14364            self.write_keyword("BETWEEN");
14365            self.write_space();
14366            self.generate_window_frame_bound(&frame.start, frame.start_side_text.as_deref())?;
14367            self.write_space();
14368            self.write_keyword("AND");
14369            self.write_space();
14370            self.generate_window_frame_bound(end, frame.end_side_text.as_deref())?;
14371        } else if should_normalize {
14372            // Normalize single-bound to BETWEEN form: ROWS 1 PRECEDING → ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
14373            self.write_keyword("BETWEEN");
14374            self.write_space();
14375            self.generate_window_frame_bound(&frame.start, frame.start_side_text.as_deref())?;
14376            self.write_space();
14377            self.write_keyword("AND");
14378            self.write_space();
14379            self.write_keyword("CURRENT ROW");
14380        } else {
14381            // Single bound format: RANGE CURRENT ROW
14382            self.generate_window_frame_bound(&frame.start, frame.start_side_text.as_deref())?;
14383        }
14384
14385        // EXCLUDE clause
14386        if let Some(exclude) = &frame.exclude {
14387            self.write_space();
14388            self.write_keyword("EXCLUDE");
14389            self.write_space();
14390            match exclude {
14391                WindowFrameExclude::CurrentRow => self.write_keyword("CURRENT ROW"),
14392                WindowFrameExclude::Group => self.write_keyword("GROUP"),
14393                WindowFrameExclude::Ties => self.write_keyword("TIES"),
14394                WindowFrameExclude::NoOthers => self.write_keyword("NO OTHERS"),
14395            }
14396        }
14397
14398        Ok(())
14399    }
14400
14401    fn generate_window_frame_bound(&mut self, bound: &WindowFrameBound, side_text: Option<&str>) -> Result<()> {
14402        // Exasol uses lowercase for preceding/following
14403        let lowercase_frame = self.config.lowercase_window_frame_keywords;
14404
14405        match bound {
14406            WindowFrameBound::CurrentRow => {
14407                self.write_keyword("CURRENT ROW");
14408            }
14409            WindowFrameBound::UnboundedPreceding => {
14410                self.write_keyword("UNBOUNDED");
14411                self.write_space();
14412                if lowercase_frame {
14413                    self.write("preceding");
14414                } else if let Some(text) = side_text {
14415                    self.write(text);
14416                } else {
14417                    self.write_keyword("PRECEDING");
14418                }
14419            }
14420            WindowFrameBound::UnboundedFollowing => {
14421                self.write_keyword("UNBOUNDED");
14422                self.write_space();
14423                if lowercase_frame {
14424                    self.write("following");
14425                } else if let Some(text) = side_text {
14426                    self.write(text);
14427                } else {
14428                    self.write_keyword("FOLLOWING");
14429                }
14430            }
14431            WindowFrameBound::Preceding(expr) => {
14432                self.generate_expression(expr)?;
14433                self.write_space();
14434                if lowercase_frame {
14435                    self.write("preceding");
14436                } else if let Some(text) = side_text {
14437                    self.write(text);
14438                } else {
14439                    self.write_keyword("PRECEDING");
14440                }
14441            }
14442            WindowFrameBound::Following(expr) => {
14443                self.generate_expression(expr)?;
14444                self.write_space();
14445                if lowercase_frame {
14446                    self.write("following");
14447                } else if let Some(text) = side_text {
14448                    self.write(text);
14449                } else {
14450                    self.write_keyword("FOLLOWING");
14451                }
14452            }
14453            WindowFrameBound::BarePreceding => {
14454                if lowercase_frame {
14455                    self.write("preceding");
14456                } else if let Some(text) = side_text {
14457                    self.write(text);
14458                } else {
14459                    self.write_keyword("PRECEDING");
14460                }
14461            }
14462            WindowFrameBound::BareFollowing => {
14463                if lowercase_frame {
14464                    self.write("following");
14465                } else if let Some(text) = side_text {
14466                    self.write(text);
14467                } else {
14468                    self.write_keyword("FOLLOWING");
14469                }
14470            }
14471            WindowFrameBound::Value(expr) => {
14472                // Bare numeric bound without PRECEDING/FOLLOWING
14473                self.generate_expression(expr)?;
14474            }
14475        }
14476        Ok(())
14477    }
14478
14479    fn generate_interval(&mut self, interval: &Interval) -> Result<()> {
14480        // For Oracle with ExprSpan: only output INTERVAL if `this` is a literal
14481        // (e.g., `(expr) DAY(9) TO SECOND(3)` should NOT have INTERVAL prefix)
14482        let skip_interval_keyword = matches!(self.config.dialect, Some(DialectType::Oracle))
14483            && matches!(&interval.unit, Some(IntervalUnitSpec::ExprSpan(_)))
14484            && !matches!(&interval.this, Some(Expression::Literal(_)));
14485
14486        if !skip_interval_keyword {
14487            self.write_keyword("INTERVAL");
14488        }
14489
14490        // Generate value if present
14491        if let Some(ref value) = interval.this {
14492            if !skip_interval_keyword {
14493                self.write_space();
14494            }
14495            // If the value is a complex expression (not a literal/column/function call)
14496            // and there's a unit, wrap it in parentheses
14497            // e.g., INTERVAL (2 * 2) MONTH, INTERVAL (DAYOFMONTH(dt) - 1) DAY
14498            let needs_parens = interval.unit.is_some() && matches!(value,
14499                Expression::Add(_) | Expression::Sub(_) | Expression::Mul(_) |
14500                Expression::Div(_) | Expression::Mod(_) | Expression::BitwiseAnd(_) |
14501                Expression::BitwiseOr(_) | Expression::BitwiseXor(_)
14502            );
14503            if needs_parens {
14504                self.write("(");
14505            }
14506            self.generate_expression(value)?;
14507            if needs_parens {
14508                self.write(")");
14509            }
14510        }
14511
14512        // Generate unit if present
14513        if let Some(ref unit_spec) = interval.unit {
14514            self.write_space();
14515            self.write_interval_unit_spec(unit_spec)?;
14516        }
14517
14518        Ok(())
14519    }
14520
14521    fn write_interval_unit_spec(&mut self, unit_spec: &IntervalUnitSpec) -> Result<()> {
14522        match unit_spec {
14523            IntervalUnitSpec::Simple { unit, use_plural } => {
14524                // If dialect doesn't allow plural forms, force singular
14525                let effective_plural = *use_plural && self.config.interval_allows_plural_form;
14526                self.write_simple_interval_unit(unit, effective_plural);
14527            }
14528            IntervalUnitSpec::Span(span) => {
14529                self.write_simple_interval_unit(&span.this, false);
14530                self.write_space();
14531                self.write_keyword("TO");
14532                self.write_space();
14533                self.write_simple_interval_unit(&span.expression, false);
14534            }
14535            IntervalUnitSpec::ExprSpan(span) => {
14536                // Expression-based interval span (e.g., DAY(9) TO SECOND(3))
14537                self.generate_expression(&span.this)?;
14538                self.write_space();
14539                self.write_keyword("TO");
14540                self.write_space();
14541                self.generate_expression(&span.expression)?;
14542            }
14543            IntervalUnitSpec::Expr(expr) => {
14544                self.generate_expression(expr)?;
14545            }
14546        }
14547        Ok(())
14548    }
14549
14550    fn write_simple_interval_unit(&mut self, unit: &IntervalUnit, use_plural: bool) {
14551        // Output interval unit, respecting plural preference
14552        match (unit, use_plural) {
14553            (IntervalUnit::Year, false) => self.write_keyword("YEAR"),
14554            (IntervalUnit::Year, true) => self.write_keyword("YEARS"),
14555            (IntervalUnit::Quarter, false) => self.write_keyword("QUARTER"),
14556            (IntervalUnit::Quarter, true) => self.write_keyword("QUARTERS"),
14557            (IntervalUnit::Month, false) => self.write_keyword("MONTH"),
14558            (IntervalUnit::Month, true) => self.write_keyword("MONTHS"),
14559            (IntervalUnit::Week, false) => self.write_keyword("WEEK"),
14560            (IntervalUnit::Week, true) => self.write_keyword("WEEKS"),
14561            (IntervalUnit::Day, false) => self.write_keyword("DAY"),
14562            (IntervalUnit::Day, true) => self.write_keyword("DAYS"),
14563            (IntervalUnit::Hour, false) => self.write_keyword("HOUR"),
14564            (IntervalUnit::Hour, true) => self.write_keyword("HOURS"),
14565            (IntervalUnit::Minute, false) => self.write_keyword("MINUTE"),
14566            (IntervalUnit::Minute, true) => self.write_keyword("MINUTES"),
14567            (IntervalUnit::Second, false) => self.write_keyword("SECOND"),
14568            (IntervalUnit::Second, true) => self.write_keyword("SECONDS"),
14569            (IntervalUnit::Millisecond, false) => self.write_keyword("MILLISECOND"),
14570            (IntervalUnit::Millisecond, true) => self.write_keyword("MILLISECONDS"),
14571            (IntervalUnit::Microsecond, false) => self.write_keyword("MICROSECOND"),
14572            (IntervalUnit::Microsecond, true) => self.write_keyword("MICROSECONDS"),
14573        }
14574    }
14575
14576    /// Normalize a date part expression to unquoted uppercase for Redshift DATEDIFF/DATEADD
14577    /// Converts: 'day', 'days', day, days, DAY -> DAY (unquoted)
14578    fn write_redshift_date_part(&mut self, expr: &Expression) {
14579        let part_str = self.extract_date_part_string(expr);
14580        if let Some(part) = part_str {
14581            let normalized = self.normalize_date_part(&part);
14582            self.write_keyword(&normalized);
14583        } else {
14584            // If we can't extract a date part string, fall back to generating the expression
14585            let _ = self.generate_expression(expr);
14586        }
14587    }
14588
14589    /// Normalize a date part expression to quoted uppercase for Redshift DATE_TRUNC
14590    /// Converts: 'day', day, DAY -> 'DAY' (quoted)
14591    fn write_redshift_date_part_quoted(&mut self, expr: &Expression) {
14592        let part_str = self.extract_date_part_string(expr);
14593        if let Some(part) = part_str {
14594            let normalized = self.normalize_date_part(&part);
14595            self.write("'");
14596            self.write(&normalized);
14597            self.write("'");
14598        } else {
14599            // If we can't extract a date part string, fall back to generating the expression
14600            let _ = self.generate_expression(expr);
14601        }
14602    }
14603
14604    /// Extract date part string from expression (handles string literals and identifiers)
14605    fn extract_date_part_string(&self, expr: &Expression) -> Option<String> {
14606        match expr {
14607            Expression::Literal(crate::expressions::Literal::String(s)) => Some(s.clone()),
14608            Expression::Identifier(id) => Some(id.name.clone()),
14609            Expression::Column(col) if col.table.is_none() => {
14610                // Simple column reference without table prefix, treat as identifier
14611                Some(col.name.name.clone())
14612            }
14613            _ => None,
14614        }
14615    }
14616
14617    /// Normalize date part to uppercase singular form
14618    /// days -> DAY, months -> MONTH, etc.
14619    fn normalize_date_part(&self, part: &str) -> String {
14620        let lower = part.to_lowercase();
14621        match lower.as_str() {
14622            "day" | "days" | "d" => "DAY".to_string(),
14623            "month" | "months" | "mon" | "mm" => "MONTH".to_string(),
14624            "year" | "years" | "y" | "yy" | "yyyy" => "YEAR".to_string(),
14625            "week" | "weeks" | "w" | "wk" => "WEEK".to_string(),
14626            "hour" | "hours" | "h" | "hh" => "HOUR".to_string(),
14627            "minute" | "minutes" | "m" | "mi" | "n" => "MINUTE".to_string(),
14628            "second" | "seconds" | "s" | "ss" => "SECOND".to_string(),
14629            "millisecond" | "milliseconds" | "ms" => "MILLISECOND".to_string(),
14630            "microsecond" | "microseconds" | "us" => "MICROSECOND".to_string(),
14631            "quarter" | "quarters" | "q" | "qq" => "QUARTER".to_string(),
14632            _ => part.to_uppercase(),
14633        }
14634    }
14635
14636    fn write_datetime_field(&mut self, field: &DateTimeField) {
14637        match field {
14638            DateTimeField::Year => self.write_keyword("YEAR"),
14639            DateTimeField::Month => self.write_keyword("MONTH"),
14640            DateTimeField::Day => self.write_keyword("DAY"),
14641            DateTimeField::Hour => self.write_keyword("HOUR"),
14642            DateTimeField::Minute => self.write_keyword("MINUTE"),
14643            DateTimeField::Second => self.write_keyword("SECOND"),
14644            DateTimeField::Millisecond => self.write_keyword("MILLISECOND"),
14645            DateTimeField::Microsecond => self.write_keyword("MICROSECOND"),
14646            DateTimeField::DayOfWeek => {
14647                let name = match self.config.dialect {
14648                    Some(DialectType::DuckDB) | Some(DialectType::Snowflake) => "DAYOFWEEK",
14649                    _ => "DOW",
14650                };
14651                self.write_keyword(name);
14652            }
14653            DateTimeField::DayOfYear => {
14654                let name = match self.config.dialect {
14655                    Some(DialectType::DuckDB) | Some(DialectType::Snowflake) => "DAYOFYEAR",
14656                    _ => "DOY",
14657                };
14658                self.write_keyword(name);
14659            }
14660            DateTimeField::Week => self.write_keyword("WEEK"),
14661            DateTimeField::WeekWithModifier(modifier) => {
14662                self.write_keyword("WEEK");
14663                self.write("(");
14664                self.write(modifier);
14665                self.write(")");
14666            }
14667            DateTimeField::Quarter => self.write_keyword("QUARTER"),
14668            DateTimeField::Epoch => self.write_keyword("EPOCH"),
14669            DateTimeField::Timezone => self.write_keyword("TIMEZONE"),
14670            DateTimeField::TimezoneHour => self.write_keyword("TIMEZONE_HOUR"),
14671            DateTimeField::TimezoneMinute => self.write_keyword("TIMEZONE_MINUTE"),
14672            DateTimeField::Date => self.write_keyword("DATE"),
14673            DateTimeField::Time => self.write_keyword("TIME"),
14674            DateTimeField::Custom(name) => self.write(name),
14675        }
14676    }
14677
14678    /// Write datetime field in lowercase (for Spark/Hive/Databricks)
14679    fn write_datetime_field_lower(&mut self, field: &DateTimeField) {
14680        match field {
14681            DateTimeField::Year => self.write("year"),
14682            DateTimeField::Month => self.write("month"),
14683            DateTimeField::Day => self.write("day"),
14684            DateTimeField::Hour => self.write("hour"),
14685            DateTimeField::Minute => self.write("minute"),
14686            DateTimeField::Second => self.write("second"),
14687            DateTimeField::Millisecond => self.write("millisecond"),
14688            DateTimeField::Microsecond => self.write("microsecond"),
14689            DateTimeField::DayOfWeek => self.write("dow"),
14690            DateTimeField::DayOfYear => self.write("doy"),
14691            DateTimeField::Week => self.write("week"),
14692            DateTimeField::WeekWithModifier(modifier) => {
14693                self.write("week(");
14694                self.write(modifier);
14695                self.write(")");
14696            }
14697            DateTimeField::Quarter => self.write("quarter"),
14698            DateTimeField::Epoch => self.write("epoch"),
14699            DateTimeField::Timezone => self.write("timezone"),
14700            DateTimeField::TimezoneHour => self.write("timezone_hour"),
14701            DateTimeField::TimezoneMinute => self.write("timezone_minute"),
14702            DateTimeField::Date => self.write("date"),
14703            DateTimeField::Time => self.write("time"),
14704            DateTimeField::Custom(name) => self.write(name),
14705        }
14706    }
14707
14708    // Helper function generators
14709
14710    fn generate_simple_func(&mut self, name: &str, arg: &Expression) -> Result<()> {
14711        self.write_keyword(name);
14712        self.write("(");
14713        self.generate_expression(arg)?;
14714        self.write(")");
14715        Ok(())
14716    }
14717
14718    /// Generate a unary function, using the original name if available for round-trip preservation
14719    fn generate_unary_func(&mut self, default_name: &str, f: &crate::expressions::UnaryFunc) -> Result<()> {
14720        let name = f.original_name.as_deref().unwrap_or(default_name);
14721        self.write_keyword(name);
14722        self.write("(");
14723        self.generate_expression(&f.this)?;
14724        self.write(")");
14725        Ok(())
14726    }
14727
14728    /// Generate SQRT/CBRT - always use function form (matches Python SQLGlot normalization)
14729    fn generate_sqrt_cbrt(&mut self, f: &crate::expressions::UnaryFunc, func_name: &str, _op: &str) -> Result<()> {
14730        // Python SQLGlot normalizes |/ and ||/ to SQRT() and CBRT()
14731        // Always use function syntax for consistency
14732        self.write_keyword(func_name);
14733        self.write("(");
14734        self.generate_expression(&f.this)?;
14735        self.write(")");
14736        Ok(())
14737    }
14738
14739    fn generate_binary_func(&mut self, name: &str, arg1: &Expression, arg2: &Expression) -> Result<()> {
14740        self.write_keyword(name);
14741        self.write("(");
14742        self.generate_expression(arg1)?;
14743        self.write(", ");
14744        self.generate_expression(arg2)?;
14745        self.write(")");
14746        Ok(())
14747    }
14748
14749    /// Generate CHAR/CHR function with optional USING charset
14750    /// e.g., CHAR(77, 77.3, '77.3' USING utf8mb4)
14751    /// e.g., CHR(187 USING NCHAR_CS) -- Oracle
14752    fn generate_char_func(&mut self, f: &crate::expressions::CharFunc) -> Result<()> {
14753        // Use stored name if available, otherwise default to CHAR
14754        let func_name = f.name.as_deref().unwrap_or("CHAR");
14755        self.write_keyword(func_name);
14756        self.write("(");
14757        for (i, arg) in f.args.iter().enumerate() {
14758            if i > 0 {
14759                self.write(", ");
14760            }
14761            self.generate_expression(arg)?;
14762        }
14763        if let Some(ref charset) = f.charset {
14764            self.write(" ");
14765            self.write_keyword("USING");
14766            self.write(" ");
14767            self.write(charset);
14768        }
14769        self.write(")");
14770        Ok(())
14771    }
14772
14773    fn generate_power(&mut self, f: &BinaryFunc) -> Result<()> {
14774        use crate::dialects::DialectType;
14775
14776        match self.config.dialect {
14777            Some(DialectType::Teradata) => {
14778                // Teradata uses ** operator for exponentiation
14779                self.generate_expression(&f.this)?;
14780                self.write(" ** ");
14781                self.generate_expression(&f.expression)?;
14782                Ok(())
14783            }
14784            _ => {
14785                // Other dialects use POWER function
14786                self.generate_binary_func("POWER", &f.this, &f.expression)
14787            }
14788        }
14789    }
14790
14791    fn generate_vararg_func(&mut self, name: &str, args: &[Expression]) -> Result<()> {
14792        self.write_keyword(name);
14793        self.write("(");
14794        for (i, arg) in args.iter().enumerate() {
14795            if i > 0 {
14796                self.write(", ");
14797            }
14798            self.generate_expression(arg)?;
14799        }
14800        self.write(")");
14801        Ok(())
14802    }
14803
14804    // String function generators
14805
14806    fn generate_concat_ws(&mut self, f: &ConcatWs) -> Result<()> {
14807        self.write_keyword("CONCAT_WS");
14808        self.write("(");
14809        self.generate_expression(&f.separator)?;
14810        for expr in &f.expressions {
14811            self.write(", ");
14812            self.generate_expression(expr)?;
14813        }
14814        self.write(")");
14815        Ok(())
14816    }
14817
14818    fn generate_substring(&mut self, f: &SubstringFunc) -> Result<()> {
14819        self.write_keyword("SUBSTRING");
14820        self.write("(");
14821        self.generate_expression(&f.this)?;
14822        // Spark/Hive use comma syntax, not FROM/FOR syntax
14823        let use_comma_syntax = matches!(
14824            self.config.dialect,
14825            Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)
14826        );
14827        if f.from_for_syntax && !use_comma_syntax {
14828            // SQL standard syntax: SUBSTRING(str FROM pos FOR len)
14829            self.write_space();
14830            self.write_keyword("FROM");
14831            self.write_space();
14832            self.generate_expression(&f.start)?;
14833            if let Some(length) = &f.length {
14834                self.write_space();
14835                self.write_keyword("FOR");
14836                self.write_space();
14837                self.generate_expression(length)?;
14838            }
14839        } else {
14840            // Comma-separated syntax: SUBSTRING(str, pos, len)
14841            self.write(", ");
14842            self.generate_expression(&f.start)?;
14843            if let Some(length) = &f.length {
14844                self.write(", ");
14845                self.generate_expression(length)?;
14846            }
14847        }
14848        self.write(")");
14849        Ok(())
14850    }
14851
14852    fn generate_overlay(&mut self, f: &OverlayFunc) -> Result<()> {
14853        self.write_keyword("OVERLAY");
14854        self.write("(");
14855        self.generate_expression(&f.this)?;
14856        self.write_space();
14857        self.write_keyword("PLACING");
14858        self.write_space();
14859        self.generate_expression(&f.replacement)?;
14860        self.write_space();
14861        self.write_keyword("FROM");
14862        self.write_space();
14863        self.generate_expression(&f.from)?;
14864        if let Some(length) = &f.length {
14865            self.write_space();
14866            self.write_keyword("FOR");
14867            self.write_space();
14868            self.generate_expression(length)?;
14869        }
14870        self.write(")");
14871        Ok(())
14872    }
14873
14874    fn generate_trim(&mut self, f: &TrimFunc) -> Result<()> {
14875        // Special case: TRIM(LEADING str) -> LTRIM(str), TRIM(TRAILING str) -> RTRIM(str)
14876        // when no characters are specified (PostgreSQL style)
14877        if f.position_explicit && f.characters.is_none() {
14878            match f.position {
14879                TrimPosition::Leading => {
14880                    self.write_keyword("LTRIM");
14881                    self.write("(");
14882                    self.generate_expression(&f.this)?;
14883                    self.write(")");
14884                    return Ok(());
14885                }
14886                TrimPosition::Trailing => {
14887                    self.write_keyword("RTRIM");
14888                    self.write("(");
14889                    self.generate_expression(&f.this)?;
14890                    self.write(")");
14891                    return Ok(());
14892                }
14893                TrimPosition::Both => {
14894                    // TRIM(BOTH str) -> BTRIM(str) in PostgreSQL, but TRIM(str) is more standard
14895                    // Fall through to standard TRIM handling
14896                }
14897            }
14898        }
14899
14900        self.write_keyword("TRIM");
14901        self.write("(");
14902        // When BOTH is specified without trim characters, simplify to just TRIM(str)
14903        let use_standard = f.sql_standard_syntax
14904            && !(f.position_explicit && f.characters.is_none() && matches!(f.position, TrimPosition::Both));
14905        if use_standard {
14906            // SQL standard syntax: TRIM(BOTH chars FROM str)
14907            // Only output position if it was explicitly specified
14908            if f.position_explicit {
14909                match f.position {
14910                    TrimPosition::Both => self.write_keyword("BOTH"),
14911                    TrimPosition::Leading => self.write_keyword("LEADING"),
14912                    TrimPosition::Trailing => self.write_keyword("TRAILING"),
14913                }
14914                self.write_space();
14915            }
14916            if let Some(chars) = &f.characters {
14917                self.generate_expression(chars)?;
14918                self.write_space();
14919            }
14920            self.write_keyword("FROM");
14921            self.write_space();
14922            self.generate_expression(&f.this)?;
14923        } else {
14924            // Simple function syntax: TRIM(str) or TRIM(str, chars)
14925            self.generate_expression(&f.this)?;
14926            if let Some(chars) = &f.characters {
14927                self.write(", ");
14928                self.generate_expression(chars)?;
14929            }
14930        }
14931        self.write(")");
14932        Ok(())
14933    }
14934
14935    fn generate_replace(&mut self, f: &ReplaceFunc) -> Result<()> {
14936        self.write_keyword("REPLACE");
14937        self.write("(");
14938        self.generate_expression(&f.this)?;
14939        self.write(", ");
14940        self.generate_expression(&f.old)?;
14941        self.write(", ");
14942        self.generate_expression(&f.new)?;
14943        self.write(")");
14944        Ok(())
14945    }
14946
14947    fn generate_left_right(&mut self, name: &str, f: &LeftRightFunc) -> Result<()> {
14948        self.write_keyword(name);
14949        self.write("(");
14950        self.generate_expression(&f.this)?;
14951        self.write(", ");
14952        self.generate_expression(&f.length)?;
14953        self.write(")");
14954        Ok(())
14955    }
14956
14957    fn generate_repeat(&mut self, f: &RepeatFunc) -> Result<()> {
14958        self.write_keyword("REPEAT");
14959        self.write("(");
14960        self.generate_expression(&f.this)?;
14961        self.write(", ");
14962        self.generate_expression(&f.times)?;
14963        self.write(")");
14964        Ok(())
14965    }
14966
14967    fn generate_pad(&mut self, name: &str, f: &PadFunc) -> Result<()> {
14968        self.write_keyword(name);
14969        self.write("(");
14970        self.generate_expression(&f.this)?;
14971        self.write(", ");
14972        self.generate_expression(&f.length)?;
14973        if let Some(fill) = &f.fill {
14974            self.write(", ");
14975            self.generate_expression(fill)?;
14976        }
14977        self.write(")");
14978        Ok(())
14979    }
14980
14981    fn generate_split(&mut self, f: &SplitFunc) -> Result<()> {
14982        self.write_keyword("SPLIT");
14983        self.write("(");
14984        self.generate_expression(&f.this)?;
14985        self.write(", ");
14986        self.generate_expression(&f.delimiter)?;
14987        self.write(")");
14988        Ok(())
14989    }
14990
14991    fn generate_regexp_like(&mut self, f: &RegexpFunc) -> Result<()> {
14992        use crate::dialects::DialectType;
14993        // PostgreSQL uses ~ operator for regex matching
14994        if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) && f.flags.is_none() {
14995            self.generate_expression(&f.this)?;
14996            self.write(" ~ ");
14997            self.generate_expression(&f.pattern)?;
14998        } else if matches!(self.config.dialect, Some(DialectType::SingleStore) | Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)) && f.flags.is_none() {
14999            // SingleStore/Spark/Hive/Databricks use RLIKE infix operator
15000            self.generate_expression(&f.this)?;
15001            self.write_keyword(" RLIKE ");
15002            self.generate_expression(&f.pattern)?;
15003        } else if matches!(self.config.dialect, Some(DialectType::StarRocks)) {
15004            // StarRocks uses REGEXP function syntax
15005            self.write_keyword("REGEXP");
15006            self.write("(");
15007            self.generate_expression(&f.this)?;
15008            self.write(", ");
15009            self.generate_expression(&f.pattern)?;
15010            if let Some(flags) = &f.flags {
15011                self.write(", ");
15012                self.generate_expression(flags)?;
15013            }
15014            self.write(")");
15015        } else {
15016            self.write_keyword("REGEXP_LIKE");
15017            self.write("(");
15018            self.generate_expression(&f.this)?;
15019            self.write(", ");
15020            self.generate_expression(&f.pattern)?;
15021            if let Some(flags) = &f.flags {
15022                self.write(", ");
15023                self.generate_expression(flags)?;
15024            }
15025            self.write(")");
15026        }
15027        Ok(())
15028    }
15029
15030    fn generate_regexp_replace(&mut self, f: &RegexpReplaceFunc) -> Result<()> {
15031        self.write_keyword("REGEXP_REPLACE");
15032        self.write("(");
15033        self.generate_expression(&f.this)?;
15034        self.write(", ");
15035        self.generate_expression(&f.pattern)?;
15036        self.write(", ");
15037        self.generate_expression(&f.replacement)?;
15038        if let Some(flags) = &f.flags {
15039            self.write(", ");
15040            self.generate_expression(flags)?;
15041        }
15042        self.write(")");
15043        Ok(())
15044    }
15045
15046    fn generate_regexp_extract(&mut self, f: &RegexpExtractFunc) -> Result<()> {
15047        self.write_keyword("REGEXP_EXTRACT");
15048        self.write("(");
15049        self.generate_expression(&f.this)?;
15050        self.write(", ");
15051        self.generate_expression(&f.pattern)?;
15052        if let Some(group) = &f.group {
15053            self.write(", ");
15054            self.generate_expression(group)?;
15055        }
15056        self.write(")");
15057        Ok(())
15058    }
15059
15060    // Math function generators
15061
15062    fn generate_round(&mut self, f: &RoundFunc) -> Result<()> {
15063        self.write_keyword("ROUND");
15064        self.write("(");
15065        self.generate_expression(&f.this)?;
15066        if let Some(decimals) = &f.decimals {
15067            self.write(", ");
15068            self.generate_expression(decimals)?;
15069        }
15070        self.write(")");
15071        Ok(())
15072    }
15073
15074    fn generate_floor(&mut self, f: &FloorFunc) -> Result<()> {
15075        self.write_keyword("FLOOR");
15076        self.write("(");
15077        self.generate_expression(&f.this)?;
15078        // Handle Druid-style FLOOR(time TO unit) syntax
15079        if let Some(to) = &f.to {
15080            self.write(" ");
15081            self.write_keyword("TO");
15082            self.write(" ");
15083            self.generate_expression(to)?;
15084        } else if let Some(scale) = &f.scale {
15085            self.write(", ");
15086            self.generate_expression(scale)?;
15087        }
15088        self.write(")");
15089        Ok(())
15090    }
15091
15092    fn generate_ceil(&mut self, f: &CeilFunc) -> Result<()> {
15093        self.write_keyword("CEIL");
15094        self.write("(");
15095        self.generate_expression(&f.this)?;
15096        // Handle Druid-style CEIL(time TO unit) syntax
15097        if let Some(to) = &f.to {
15098            self.write(" ");
15099            self.write_keyword("TO");
15100            self.write(" ");
15101            self.generate_expression(to)?;
15102        } else if let Some(decimals) = &f.decimals {
15103            self.write(", ");
15104            self.generate_expression(decimals)?;
15105        }
15106        self.write(")");
15107        Ok(())
15108    }
15109
15110    fn generate_log(&mut self, f: &LogFunc) -> Result<()> {
15111        self.write_keyword("LOG");
15112        self.write("(");
15113        if let Some(base) = &f.base {
15114            self.generate_expression(base)?;
15115            self.write(", ");
15116        }
15117        self.generate_expression(&f.this)?;
15118        self.write(")");
15119        Ok(())
15120    }
15121
15122    // Date/time function generators
15123
15124    fn generate_current_time(&mut self, f: &CurrentTime) -> Result<()> {
15125        self.write_keyword("CURRENT_TIME");
15126        if let Some(precision) = f.precision {
15127            self.write(&format!("({})", precision));
15128        }
15129        Ok(())
15130    }
15131
15132    fn generate_current_timestamp(&mut self, f: &CurrentTimestamp) -> Result<()> {
15133        use crate::dialects::DialectType;
15134
15135        // Oracle/Redshift SYSDATE handling
15136        if f.sysdate {
15137            match self.config.dialect {
15138                Some(DialectType::Oracle) | Some(DialectType::Redshift) => {
15139                    self.write_keyword("SYSDATE");
15140                    return Ok(());
15141                }
15142                Some(DialectType::Snowflake) => {
15143                    // Snowflake uses SYSDATE() function
15144                    self.write_keyword("SYSDATE");
15145                    self.write("()");
15146                    return Ok(());
15147                }
15148                _ => {
15149                    // Other dialects use CURRENT_TIMESTAMP for SYSDATE
15150                }
15151            }
15152        }
15153
15154        self.write_keyword("CURRENT_TIMESTAMP");
15155        // MySQL, Spark, Hive always use CURRENT_TIMESTAMP() with parentheses
15156        if let Some(precision) = f.precision {
15157            self.write(&format!("({})", precision));
15158        } else if matches!(
15159            self.config.dialect,
15160            Some(crate::dialects::DialectType::MySQL) | Some(crate::dialects::DialectType::SingleStore) |
15161            Some(crate::dialects::DialectType::TiDB) | Some(crate::dialects::DialectType::Spark) |
15162            Some(crate::dialects::DialectType::Hive) |
15163            Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::ClickHouse) |
15164            Some(crate::dialects::DialectType::BigQuery) | Some(crate::dialects::DialectType::Snowflake)
15165        ) {
15166            self.write("()");
15167        }
15168        Ok(())
15169    }
15170
15171    fn generate_at_time_zone(&mut self, f: &AtTimeZone) -> Result<()> {
15172        // Exasol uses CONVERT_TZ(timestamp, 'UTC', zone) instead of AT TIME ZONE
15173        if self.config.dialect == Some(DialectType::Exasol) {
15174            self.write_keyword("CONVERT_TZ");
15175            self.write("(");
15176            self.generate_expression(&f.this)?;
15177            self.write(", 'UTC', ");
15178            self.generate_expression(&f.zone)?;
15179            self.write(")");
15180            return Ok(());
15181        }
15182
15183        self.generate_expression(&f.this)?;
15184        self.write_space();
15185        self.write_keyword("AT TIME ZONE");
15186        self.write_space();
15187        self.generate_expression(&f.zone)?;
15188        Ok(())
15189    }
15190
15191    fn generate_date_add(&mut self, f: &DateAddFunc, name: &str) -> Result<()> {
15192        use crate::dialects::DialectType;
15193
15194        // Presto/Trino use DATE_ADD('unit', interval, date) format
15195        // with the interval cast to BIGINT when needed
15196        let is_presto_like = matches!(
15197            self.config.dialect,
15198            Some(DialectType::Presto) | Some(DialectType::Trino)
15199        );
15200
15201        if is_presto_like {
15202            self.write_keyword(name);
15203            self.write("(");
15204            // Unit as string literal
15205            self.write("'");
15206            self.write_simple_interval_unit(&f.unit, false);
15207            self.write("'");
15208            self.write(", ");
15209            // Interval - wrap in CAST(...AS BIGINT) if it doesn't return integer type
15210            let needs_cast = !self.returns_integer_type(&f.interval);
15211            if needs_cast {
15212                self.write_keyword("CAST");
15213                self.write("(");
15214            }
15215            self.generate_expression(&f.interval)?;
15216            if needs_cast {
15217                self.write_space();
15218                self.write_keyword("AS");
15219                self.write_space();
15220                self.write_keyword("BIGINT");
15221                self.write(")");
15222            }
15223            self.write(", ");
15224            self.generate_expression(&f.this)?;
15225            self.write(")");
15226        } else {
15227            self.write_keyword(name);
15228            self.write("(");
15229            self.generate_expression(&f.this)?;
15230            self.write(", ");
15231            self.write_keyword("INTERVAL");
15232            self.write_space();
15233            self.generate_expression(&f.interval)?;
15234            self.write_space();
15235            self.write_simple_interval_unit(&f.unit, false); // Use singular form for DATEADD
15236            self.write(")");
15237        }
15238        Ok(())
15239    }
15240
15241    /// Check if an expression returns an integer type (doesn't need cast to BIGINT in Presto DATE_ADD)
15242    /// This is a heuristic to avoid full type inference
15243    fn returns_integer_type(&self, expr: &Expression) -> bool {
15244        use crate::expressions::{DataType, Literal};
15245        match expr {
15246            // Integer literals (no decimal point)
15247            Expression::Literal(Literal::Number(n)) => !n.contains('.'),
15248
15249            // FLOOR(x) returns integer if x is integer
15250            Expression::Floor(f) => self.returns_integer_type(&f.this),
15251
15252            // ROUND(x) returns integer if x is integer
15253            Expression::Round(f) => {
15254                // Only if no decimals arg or it's returning an integer
15255                f.decimals.is_none() && self.returns_integer_type(&f.this)
15256            }
15257
15258            // SIGN returns integer if input is integer
15259            Expression::Sign(f) => self.returns_integer_type(&f.this),
15260
15261            // ABS returns the same type as input
15262            Expression::Abs(f) => self.returns_integer_type(&f.this),
15263
15264            // Arithmetic operations on integers return integers
15265            Expression::Mul(op) => self.returns_integer_type(&op.left) && self.returns_integer_type(&op.right),
15266            Expression::Add(op) => self.returns_integer_type(&op.left) && self.returns_integer_type(&op.right),
15267            Expression::Sub(op) => self.returns_integer_type(&op.left) && self.returns_integer_type(&op.right),
15268            Expression::Mod(op) => self.returns_integer_type(&op.left),
15269
15270            // CAST(x AS BIGINT/INT/INTEGER/SMALLINT/TINYINT) returns integer
15271            Expression::Cast(c) => matches!(&c.to,
15272                DataType::BigInt { .. } | DataType::Int { .. } |
15273                DataType::SmallInt { .. } | DataType::TinyInt { .. }
15274            ),
15275
15276            // Negation: -x returns integer if x is integer
15277            Expression::Neg(op) => self.returns_integer_type(&op.this),
15278
15279            // Parenthesized expression
15280            Expression::Paren(p) => self.returns_integer_type(&p.this),
15281
15282            // Column references and most expressions are assumed to need casting
15283            // since we don't have full type information
15284            _ => false,
15285        }
15286    }
15287
15288    fn generate_datediff(&mut self, f: &DateDiffFunc) -> Result<()> {
15289        self.write_keyword("DATEDIFF");
15290        self.write("(");
15291        if let Some(unit) = &f.unit {
15292            self.write_simple_interval_unit(unit, false); // Use singular form for DATEDIFF
15293            self.write(", ");
15294        }
15295        self.generate_expression(&f.this)?;
15296        self.write(", ");
15297        self.generate_expression(&f.expression)?;
15298        self.write(")");
15299        Ok(())
15300    }
15301
15302    fn generate_date_trunc(&mut self, f: &DateTruncFunc) -> Result<()> {
15303        self.write_keyword("DATE_TRUNC");
15304        self.write("('");
15305        self.write_datetime_field(&f.unit);
15306        self.write("', ");
15307        self.generate_expression(&f.this)?;
15308        self.write(")");
15309        Ok(())
15310    }
15311
15312    fn generate_last_day(&mut self, f: &LastDayFunc) -> Result<()> {
15313        use crate::dialects::DialectType;
15314        use crate::expressions::DateTimeField;
15315
15316        self.write_keyword("LAST_DAY");
15317        self.write("(");
15318        self.generate_expression(&f.this)?;
15319        if let Some(unit) = &f.unit {
15320            self.write(", ");
15321            // BigQuery: strip week-start modifier from WEEK(SUNDAY), WEEK(MONDAY), etc.
15322            // WEEK(SUNDAY) -> WEEK
15323            if matches!(self.config.dialect, Some(DialectType::BigQuery)) {
15324                if let DateTimeField::WeekWithModifier(_) = unit {
15325                    self.write_keyword("WEEK");
15326                } else {
15327                    self.write_datetime_field(unit);
15328                }
15329            } else {
15330                self.write_datetime_field(unit);
15331            }
15332        }
15333        self.write(")");
15334        Ok(())
15335    }
15336
15337    fn generate_extract(&mut self, f: &ExtractFunc) -> Result<()> {
15338        // TSQL/Fabric use DATEPART(part, expr) instead of EXTRACT(part FROM expr)
15339        if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
15340            self.write_keyword("DATEPART");
15341            self.write("(");
15342            self.write_datetime_field(&f.field);
15343            self.write(", ");
15344            self.generate_expression(&f.this)?;
15345            self.write(")");
15346            return Ok(());
15347        }
15348        self.write_keyword("EXTRACT");
15349        self.write("(");
15350        // Hive/Spark use lowercase datetime fields in EXTRACT
15351        if matches!(self.config.dialect, Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)) {
15352            self.write_datetime_field_lower(&f.field);
15353        } else {
15354            self.write_datetime_field(&f.field);
15355        }
15356        self.write_space();
15357        self.write_keyword("FROM");
15358        self.write_space();
15359        self.generate_expression(&f.this)?;
15360        self.write(")");
15361        Ok(())
15362    }
15363
15364    fn generate_to_date(&mut self, f: &ToDateFunc) -> Result<()> {
15365        self.write_keyword("TO_DATE");
15366        self.write("(");
15367        self.generate_expression(&f.this)?;
15368        if let Some(format) = &f.format {
15369            self.write(", ");
15370            self.generate_expression(format)?;
15371        }
15372        self.write(")");
15373        Ok(())
15374    }
15375
15376    fn generate_to_timestamp(&mut self, f: &ToTimestampFunc) -> Result<()> {
15377        self.write_keyword("TO_TIMESTAMP");
15378        self.write("(");
15379        self.generate_expression(&f.this)?;
15380        if let Some(format) = &f.format {
15381            self.write(", ");
15382            self.generate_expression(format)?;
15383        }
15384        self.write(")");
15385        Ok(())
15386    }
15387
15388    // Control flow function generators
15389
15390    fn generate_if_func(&mut self, f: &IfFunc) -> Result<()> {
15391        use crate::dialects::DialectType;
15392
15393        // Exasol uses IF condition THEN true_value ELSE false_value ENDIF syntax
15394        if self.config.dialect == Some(DialectType::Exasol) {
15395            self.write_keyword("IF");
15396            self.write_space();
15397            self.generate_expression(&f.condition)?;
15398            self.write_space();
15399            self.write_keyword("THEN");
15400            self.write_space();
15401            self.generate_expression(&f.true_value)?;
15402            if let Some(false_val) = &f.false_value {
15403                self.write_space();
15404                self.write_keyword("ELSE");
15405                self.write_space();
15406                self.generate_expression(false_val)?;
15407            }
15408            self.write_space();
15409            self.write_keyword("ENDIF");
15410            return Ok(());
15411        }
15412
15413        // Choose function name based on target dialect
15414        let func_name = match self.config.dialect {
15415            Some(DialectType::Snowflake) => "IFF",
15416            Some(DialectType::SQLite) | Some(DialectType::TSQL) => "IIF",
15417            _ => "IF",
15418        };
15419        self.write_keyword(func_name);
15420        self.write("(");
15421        self.generate_expression(&f.condition)?;
15422        self.write(", ");
15423        self.generate_expression(&f.true_value)?;
15424        if let Some(false_val) = &f.false_value {
15425            self.write(", ");
15426            self.generate_expression(false_val)?;
15427        }
15428        self.write(")");
15429        Ok(())
15430    }
15431
15432    fn generate_nvl2(&mut self, f: &Nvl2Func) -> Result<()> {
15433        self.write_keyword("NVL2");
15434        self.write("(");
15435        self.generate_expression(&f.this)?;
15436        self.write(", ");
15437        self.generate_expression(&f.true_value)?;
15438        self.write(", ");
15439        self.generate_expression(&f.false_value)?;
15440        self.write(")");
15441        Ok(())
15442    }
15443
15444    // Typed aggregate function generators
15445
15446    fn generate_count(&mut self, f: &CountFunc) -> Result<()> {
15447        // Use normalize_functions for COUNT to respect ClickHouse case preservation
15448        let count_name = match self.config.normalize_functions {
15449            NormalizeFunctions::Upper => "COUNT".to_string(),
15450            NormalizeFunctions::Lower => "count".to_string(),
15451            NormalizeFunctions::None => f.original_name.clone().unwrap_or_else(|| "COUNT".to_string()),
15452        };
15453        self.write(&count_name);
15454        self.write("(");
15455        if f.distinct {
15456            self.write_keyword("DISTINCT");
15457            self.write_space();
15458        }
15459        if f.star {
15460            self.write("*");
15461        } else if let Some(ref expr) = f.this {
15462            // For COUNT(DISTINCT a, b), unwrap the Tuple to avoid extra parentheses
15463            if let Expression::Tuple(tuple) = expr {
15464                // Check if we need to transform multi-arg COUNT DISTINCT
15465                // When dialect doesn't support multi_arg_distinct, transform:
15466                // COUNT(DISTINCT a, b) -> COUNT(DISTINCT CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE (a, b) END)
15467                let needs_transform = f.distinct
15468                    && tuple.expressions.len() > 1
15469                    && !self.config.multi_arg_distinct;
15470
15471                if needs_transform {
15472                    // Generate: CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE (a, b) END
15473                    self.write_keyword("CASE");
15474                    for e in &tuple.expressions {
15475                        self.write_space();
15476                        self.write_keyword("WHEN");
15477                        self.write_space();
15478                        self.generate_expression(e)?;
15479                        self.write_space();
15480                        self.write_keyword("IS NULL THEN NULL");
15481                    }
15482                    self.write_space();
15483                    self.write_keyword("ELSE");
15484                    self.write(" (");
15485                    for (i, e) in tuple.expressions.iter().enumerate() {
15486                        if i > 0 {
15487                            self.write(", ");
15488                        }
15489                        self.generate_expression(e)?;
15490                    }
15491                    self.write(")");
15492                    self.write_space();
15493                    self.write_keyword("END");
15494                } else {
15495                    for (i, e) in tuple.expressions.iter().enumerate() {
15496                        if i > 0 {
15497                            self.write(", ");
15498                        }
15499                        self.generate_expression(e)?;
15500                    }
15501                }
15502            } else {
15503                self.generate_expression(expr)?;
15504            }
15505        }
15506        // RESPECT NULLS / IGNORE NULLS
15507        if let Some(ignore) = f.ignore_nulls {
15508            self.write_space();
15509            if ignore {
15510                self.write_keyword("IGNORE NULLS");
15511            } else {
15512                self.write_keyword("RESPECT NULLS");
15513            }
15514        }
15515        self.write(")");
15516        if let Some(ref filter) = f.filter {
15517            self.write_space();
15518            self.write_keyword("FILTER");
15519            self.write("(");
15520            self.write_keyword("WHERE");
15521            self.write_space();
15522            self.generate_expression(filter)?;
15523            self.write(")");
15524        }
15525        Ok(())
15526    }
15527
15528    fn generate_agg_func(&mut self, name: &str, f: &AggFunc) -> Result<()> {
15529        // Apply function name normalization based on config
15530        let func_name = match self.config.normalize_functions {
15531            NormalizeFunctions::Upper => name.to_uppercase(),
15532            NormalizeFunctions::Lower => name.to_lowercase(),
15533            NormalizeFunctions::None => {
15534                // Use the original function name from parsing if available,
15535                // otherwise fall back to lowercase of the hardcoded constant
15536                if let Some(ref original) = f.name {
15537                    original.clone()
15538                } else {
15539                    name.to_lowercase()
15540                }
15541            }
15542        };
15543        self.write(&func_name);
15544        self.write("(");
15545        if f.distinct {
15546            self.write_keyword("DISTINCT");
15547            self.write_space();
15548        }
15549        // Skip generating the expression if it's a NULL placeholder for zero-arg aggregates like MODE()
15550        if !matches!(f.this, Expression::Null(_)) {
15551            self.generate_expression(&f.this)?;
15552        }
15553        // Generate IGNORE NULLS / RESPECT NULLS inside parens if config says so (BigQuery style)
15554        // DuckDB doesn't support IGNORE NULLS / RESPECT NULLS in aggregate functions - skip it
15555        if self.config.ignore_nulls_in_func && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
15556            match f.ignore_nulls {
15557                Some(true) => {
15558                    self.write_space();
15559                    self.write_keyword("IGNORE NULLS");
15560                }
15561                Some(false) => {
15562                    self.write_space();
15563                    self.write_keyword("RESPECT NULLS");
15564                }
15565                None => {}
15566            }
15567        }
15568        // Generate HAVING MAX/MIN if present (BigQuery syntax)
15569        // e.g., ANY_VALUE(fruit HAVING MAX sold)
15570        if let Some((ref expr, is_max)) = f.having_max {
15571            self.write_space();
15572            self.write_keyword("HAVING");
15573            self.write_space();
15574            if is_max {
15575                self.write_keyword("MAX");
15576            } else {
15577                self.write_keyword("MIN");
15578            }
15579            self.write_space();
15580            self.generate_expression(expr)?;
15581        }
15582        // Generate ORDER BY if present (for aggregates like ARRAY_AGG(x ORDER BY y))
15583        if !f.order_by.is_empty() {
15584            self.write_space();
15585            self.write_keyword("ORDER BY");
15586            self.write_space();
15587            for (i, ord) in f.order_by.iter().enumerate() {
15588                if i > 0 { self.write(", "); }
15589                self.generate_ordered(ord)?;
15590            }
15591        }
15592        // Generate LIMIT if present (for aggregates like ARRAY_AGG(x ORDER BY y LIMIT 2))
15593        if let Some(ref limit) = f.limit {
15594            self.write_space();
15595            self.write_keyword("LIMIT");
15596            self.write_space();
15597            // Check if this is a Tuple representing LIMIT offset, count
15598            if let Expression::Tuple(t) = limit.as_ref() {
15599                if t.expressions.len() == 2 {
15600                    self.generate_expression(&t.expressions[0])?;
15601                    self.write(", ");
15602                    self.generate_expression(&t.expressions[1])?;
15603                } else {
15604                    self.generate_expression(limit)?;
15605                }
15606            } else {
15607                self.generate_expression(limit)?;
15608            }
15609        }
15610        self.write(")");
15611        // Generate IGNORE NULLS / RESPECT NULLS outside parens if config says so (standard style)
15612        // DuckDB doesn't support IGNORE NULLS / RESPECT NULLS in aggregate functions - skip it
15613        if !self.config.ignore_nulls_in_func && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
15614            match f.ignore_nulls {
15615                Some(true) => {
15616                    self.write_space();
15617                    self.write_keyword("IGNORE NULLS");
15618                }
15619                Some(false) => {
15620                    self.write_space();
15621                    self.write_keyword("RESPECT NULLS");
15622                }
15623                None => {}
15624            }
15625        }
15626        if let Some(ref filter) = f.filter {
15627            self.write_space();
15628            self.write_keyword("FILTER");
15629            self.write("(");
15630            self.write_keyword("WHERE");
15631            self.write_space();
15632            self.generate_expression(filter)?;
15633            self.write(")");
15634        }
15635        Ok(())
15636    }
15637
15638    fn generate_group_concat(&mut self, f: &GroupConcatFunc) -> Result<()> {
15639        self.write_keyword("GROUP_CONCAT");
15640        self.write("(");
15641        if f.distinct {
15642            self.write_keyword("DISTINCT");
15643            self.write_space();
15644        }
15645        self.generate_expression(&f.this)?;
15646        if let Some(ref order_by) = f.order_by {
15647            self.write_space();
15648            self.write_keyword("ORDER BY");
15649            self.write_space();
15650            for (i, ord) in order_by.iter().enumerate() {
15651                if i > 0 { self.write(", "); }
15652                self.generate_ordered(ord)?;
15653            }
15654        }
15655        if let Some(ref sep) = f.separator {
15656            // SQLite uses GROUP_CONCAT(x, sep) syntax (comma-separated)
15657            // MySQL and others use GROUP_CONCAT(x SEPARATOR sep) syntax
15658            if matches!(self.config.dialect, Some(crate::dialects::DialectType::SQLite)) {
15659                self.write(", ");
15660                self.generate_expression(sep)?;
15661            } else {
15662                self.write_space();
15663                self.write_keyword("SEPARATOR");
15664                self.write_space();
15665                self.generate_expression(sep)?;
15666            }
15667        }
15668        self.write(")");
15669        if let Some(ref filter) = f.filter {
15670            self.write_space();
15671            self.write_keyword("FILTER");
15672            self.write("(");
15673            self.write_keyword("WHERE");
15674            self.write_space();
15675            self.generate_expression(filter)?;
15676            self.write(")");
15677        }
15678        Ok(())
15679    }
15680
15681    fn generate_string_agg(&mut self, f: &StringAggFunc) -> Result<()> {
15682        let is_tsql = matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL));
15683        self.write_keyword("STRING_AGG");
15684        self.write("(");
15685        if f.distinct {
15686            self.write_keyword("DISTINCT");
15687            self.write_space();
15688        }
15689        self.generate_expression(&f.this)?;
15690        if let Some(ref separator) = f.separator {
15691            self.write(", ");
15692            self.generate_expression(separator)?;
15693        }
15694        // For TSQL, ORDER BY goes in WITHIN GROUP clause after the closing paren
15695        if !is_tsql {
15696            if let Some(ref order_by) = f.order_by {
15697                self.write_space();
15698                self.write_keyword("ORDER BY");
15699                self.write_space();
15700                for (i, ord) in order_by.iter().enumerate() {
15701                    if i > 0 { self.write(", "); }
15702                    self.generate_ordered(ord)?;
15703                }
15704            }
15705        }
15706        if let Some(ref limit) = f.limit {
15707            self.write_space();
15708            self.write_keyword("LIMIT");
15709            self.write_space();
15710            self.generate_expression(limit)?;
15711        }
15712        self.write(")");
15713        // TSQL uses WITHIN GROUP (ORDER BY ...) after the function call
15714        if is_tsql {
15715            if let Some(ref order_by) = f.order_by {
15716                self.write_space();
15717                self.write_keyword("WITHIN GROUP");
15718                self.write(" (");
15719                self.write_keyword("ORDER BY");
15720                self.write_space();
15721                for (i, ord) in order_by.iter().enumerate() {
15722                    if i > 0 { self.write(", "); }
15723                    self.generate_ordered(ord)?;
15724                }
15725                self.write(")");
15726            }
15727        }
15728        if let Some(ref filter) = f.filter {
15729            self.write_space();
15730            self.write_keyword("FILTER");
15731            self.write("(");
15732            self.write_keyword("WHERE");
15733            self.write_space();
15734            self.generate_expression(filter)?;
15735            self.write(")");
15736        }
15737        Ok(())
15738    }
15739
15740    fn generate_listagg(&mut self, f: &ListAggFunc) -> Result<()> {
15741        use crate::dialects::DialectType;
15742        self.write_keyword("LISTAGG");
15743        self.write("(");
15744        if f.distinct {
15745            self.write_keyword("DISTINCT");
15746            self.write_space();
15747        }
15748        self.generate_expression(&f.this)?;
15749        if let Some(ref sep) = f.separator {
15750            self.write(", ");
15751            self.generate_expression(sep)?;
15752        } else if matches!(self.config.dialect, Some(DialectType::Trino) | Some(DialectType::Presto)) {
15753            // Trino/Presto require explicit separator; default to ','
15754            self.write(", ','");
15755        }
15756        if let Some(ref overflow) = f.on_overflow {
15757            self.write_space();
15758            self.write_keyword("ON OVERFLOW");
15759            self.write_space();
15760            match overflow {
15761                ListAggOverflow::Error => self.write_keyword("ERROR"),
15762                ListAggOverflow::Truncate { filler, with_count } => {
15763                    self.write_keyword("TRUNCATE");
15764                    if let Some(ref fill) = filler {
15765                        self.write_space();
15766                        self.generate_expression(fill)?;
15767                    }
15768                    if *with_count {
15769                        self.write_space();
15770                        self.write_keyword("WITH COUNT");
15771                    } else {
15772                        self.write_space();
15773                        self.write_keyword("WITHOUT COUNT");
15774                    }
15775                }
15776            }
15777        }
15778        self.write(")");
15779        if let Some(ref order_by) = f.order_by {
15780            self.write_space();
15781            self.write_keyword("WITHIN GROUP");
15782            self.write(" (");
15783            self.write_keyword("ORDER BY");
15784            self.write_space();
15785            for (i, ord) in order_by.iter().enumerate() {
15786                if i > 0 { self.write(", "); }
15787                self.generate_ordered(ord)?;
15788            }
15789            self.write(")");
15790        }
15791        if let Some(ref filter) = f.filter {
15792            self.write_space();
15793            self.write_keyword("FILTER");
15794            self.write("(");
15795            self.write_keyword("WHERE");
15796            self.write_space();
15797            self.generate_expression(filter)?;
15798            self.write(")");
15799        }
15800        Ok(())
15801    }
15802
15803    fn generate_sum_if(&mut self, f: &SumIfFunc) -> Result<()> {
15804        self.write_keyword("SUM_IF");
15805        self.write("(");
15806        self.generate_expression(&f.this)?;
15807        self.write(", ");
15808        self.generate_expression(&f.condition)?;
15809        self.write(")");
15810        if let Some(ref filter) = f.filter {
15811            self.write_space();
15812            self.write_keyword("FILTER");
15813            self.write("(");
15814            self.write_keyword("WHERE");
15815            self.write_space();
15816            self.generate_expression(filter)?;
15817            self.write(")");
15818        }
15819        Ok(())
15820    }
15821
15822    fn generate_approx_percentile(&mut self, f: &ApproxPercentileFunc) -> Result<()> {
15823        self.write_keyword("APPROX_PERCENTILE");
15824        self.write("(");
15825        self.generate_expression(&f.this)?;
15826        self.write(", ");
15827        self.generate_expression(&f.percentile)?;
15828        if let Some(ref acc) = f.accuracy {
15829            self.write(", ");
15830            self.generate_expression(acc)?;
15831        }
15832        self.write(")");
15833        if let Some(ref filter) = f.filter {
15834            self.write_space();
15835            self.write_keyword("FILTER");
15836            self.write("(");
15837            self.write_keyword("WHERE");
15838            self.write_space();
15839            self.generate_expression(filter)?;
15840            self.write(")");
15841        }
15842        Ok(())
15843    }
15844
15845    fn generate_percentile(&mut self, name: &str, f: &PercentileFunc) -> Result<()> {
15846        self.write_keyword(name);
15847        self.write("(");
15848        self.generate_expression(&f.percentile)?;
15849        self.write(")");
15850        if let Some(ref order_by) = f.order_by {
15851            self.write_space();
15852            self.write_keyword("WITHIN GROUP");
15853            self.write(" (");
15854            self.write_keyword("ORDER BY");
15855            self.write_space();
15856            self.generate_expression(&f.this)?;
15857            for ord in order_by.iter() {
15858                if ord.desc {
15859                    self.write_space();
15860                    self.write_keyword("DESC");
15861                }
15862            }
15863            self.write(")");
15864        }
15865        if let Some(ref filter) = f.filter {
15866            self.write_space();
15867            self.write_keyword("FILTER");
15868            self.write("(");
15869            self.write_keyword("WHERE");
15870            self.write_space();
15871            self.generate_expression(filter)?;
15872            self.write(")");
15873        }
15874        Ok(())
15875    }
15876
15877    // Window function generators
15878
15879    fn generate_ntile(&mut self, f: &NTileFunc) -> Result<()> {
15880        self.write_keyword("NTILE");
15881        self.write("(");
15882        if let Some(num_buckets) = &f.num_buckets {
15883            self.generate_expression(num_buckets)?;
15884        }
15885        if let Some(order_by) = &f.order_by {
15886            self.write_keyword(" ORDER BY ");
15887            for (i, ob) in order_by.iter().enumerate() {
15888                if i > 0 { self.write(", "); }
15889                self.generate_ordered(ob)?;
15890            }
15891        }
15892        self.write(")");
15893        Ok(())
15894    }
15895
15896    fn generate_lead_lag(&mut self, name: &str, f: &LeadLagFunc) -> Result<()> {
15897        self.write_keyword(name);
15898        self.write("(");
15899        self.generate_expression(&f.this)?;
15900        if let Some(ref offset) = f.offset {
15901            self.write(", ");
15902            self.generate_expression(offset)?;
15903            if let Some(ref default) = f.default {
15904                self.write(", ");
15905                self.generate_expression(default)?;
15906            }
15907        }
15908        // IGNORE NULLS inside parens for dialects like BigQuery
15909        if f.ignore_nulls && self.config.ignore_nulls_in_func {
15910            self.write_space();
15911            self.write_keyword("IGNORE NULLS");
15912        }
15913        self.write(")");
15914        // IGNORE NULLS outside parens for other dialects
15915        if f.ignore_nulls && !self.config.ignore_nulls_in_func {
15916            self.write_space();
15917            self.write_keyword("IGNORE NULLS");
15918        }
15919        Ok(())
15920    }
15921
15922    fn generate_value_func(&mut self, name: &str, f: &ValueFunc) -> Result<()> {
15923        self.write_keyword(name);
15924        self.write("(");
15925        self.generate_expression(&f.this)?;
15926        // IGNORE NULLS / RESPECT NULLS inside parens for dialects like BigQuery
15927        if self.config.ignore_nulls_in_func {
15928            match f.ignore_nulls {
15929                Some(true) => {
15930                    self.write_space();
15931                    self.write_keyword("IGNORE NULLS");
15932                }
15933                Some(false) => {
15934                    self.write_space();
15935                    self.write_keyword("RESPECT NULLS");
15936                }
15937                None => {}
15938            }
15939        }
15940        self.write(")");
15941        // IGNORE NULLS / RESPECT NULLS outside parens for other dialects
15942        if !self.config.ignore_nulls_in_func {
15943            match f.ignore_nulls {
15944                Some(true) => {
15945                    self.write_space();
15946                    self.write_keyword("IGNORE NULLS");
15947                }
15948                Some(false) => {
15949                    self.write_space();
15950                    self.write_keyword("RESPECT NULLS");
15951                }
15952                None => {}
15953            }
15954        }
15955        Ok(())
15956    }
15957
15958    fn generate_nth_value(&mut self, f: &NthValueFunc) -> Result<()> {
15959        self.write_keyword("NTH_VALUE");
15960        self.write("(");
15961        self.generate_expression(&f.this)?;
15962        self.write(", ");
15963        self.generate_expression(&f.offset)?;
15964        // IGNORE NULLS / RESPECT NULLS inside parens for dialects like BigQuery
15965        if self.config.ignore_nulls_in_func {
15966            match f.ignore_nulls {
15967                Some(true) => {
15968                    self.write_space();
15969                    self.write_keyword("IGNORE NULLS");
15970                }
15971                Some(false) => {
15972                    self.write_space();
15973                    self.write_keyword("RESPECT NULLS");
15974                }
15975                None => {}
15976            }
15977        }
15978        self.write(")");
15979        // IGNORE NULLS / RESPECT NULLS outside parens for other dialects
15980        if !self.config.ignore_nulls_in_func {
15981            match f.ignore_nulls {
15982                Some(true) => {
15983                    self.write_space();
15984                    self.write_keyword("IGNORE NULLS");
15985                }
15986                Some(false) => {
15987                    self.write_space();
15988                    self.write_keyword("RESPECT NULLS");
15989                }
15990                None => {}
15991            }
15992        }
15993        Ok(())
15994    }
15995
15996    // Additional string function generators
15997
15998    fn generate_position(&mut self, f: &PositionFunc) -> Result<()> {
15999        // Standard syntax: POSITION(substr IN str)
16000        // ClickHouse prefers comma syntax with reversed arg order: POSITION(str, substr[, start])
16001        if matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) {
16002            self.write_keyword("POSITION");
16003            self.write("(");
16004            self.generate_expression(&f.string)?;
16005            self.write(", ");
16006            self.generate_expression(&f.substring)?;
16007            if let Some(ref start) = f.start {
16008                self.write(", ");
16009                self.generate_expression(start)?;
16010            }
16011            self.write(")");
16012            return Ok(());
16013        }
16014
16015        self.write_keyword("POSITION");
16016        self.write("(");
16017        self.generate_expression(&f.substring)?;
16018        self.write_space();
16019        self.write_keyword("IN");
16020        self.write_space();
16021        self.generate_expression(&f.string)?;
16022        if let Some(ref start) = f.start {
16023            self.write(", ");
16024            self.generate_expression(start)?;
16025        }
16026        self.write(")");
16027        Ok(())
16028    }
16029
16030    // Additional math function generators
16031
16032    fn generate_rand(&mut self, f: &Rand) -> Result<()> {
16033        // Teradata RANDOM(lower, upper)
16034        if f.lower.is_some() || f.upper.is_some() {
16035            self.write_keyword("RANDOM");
16036            self.write("(");
16037            if let Some(ref lower) = f.lower {
16038                self.generate_expression(lower)?;
16039            }
16040            if let Some(ref upper) = f.upper {
16041                self.write(", ");
16042                self.generate_expression(upper)?;
16043            }
16044            self.write(")");
16045            return Ok(());
16046        }
16047        // Snowflake uses RANDOM instead of RAND, DuckDB uses RANDOM without seed
16048        let func_name = match self.config.dialect {
16049            Some(crate::dialects::DialectType::Snowflake) | Some(crate::dialects::DialectType::DuckDB) => "RANDOM",
16050            _ => "RAND",
16051        };
16052        self.write_keyword(func_name);
16053        self.write("(");
16054        // DuckDB doesn't support seeded RANDOM, so skip the seed
16055        if !matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB)) {
16056            if let Some(ref seed) = f.seed {
16057                self.generate_expression(seed)?;
16058            }
16059        }
16060        self.write(")");
16061        Ok(())
16062    }
16063
16064    fn generate_truncate_func(&mut self, f: &TruncateFunc) -> Result<()> {
16065        self.write_keyword("TRUNCATE");
16066        self.write("(");
16067        self.generate_expression(&f.this)?;
16068        if let Some(ref decimals) = f.decimals {
16069            self.write(", ");
16070            self.generate_expression(decimals)?;
16071        }
16072        self.write(")");
16073        Ok(())
16074    }
16075
16076    // Control flow generators
16077
16078    fn generate_decode(&mut self, f: &DecodeFunc) -> Result<()> {
16079        self.write_keyword("DECODE");
16080        self.write("(");
16081        self.generate_expression(&f.this)?;
16082        for (search, result) in &f.search_results {
16083            self.write(", ");
16084            self.generate_expression(search)?;
16085            self.write(", ");
16086            self.generate_expression(result)?;
16087        }
16088        if let Some(ref default) = f.default {
16089            self.write(", ");
16090            self.generate_expression(default)?;
16091        }
16092        self.write(")");
16093        Ok(())
16094    }
16095
16096    // Date/time function generators
16097
16098    fn generate_date_format(&mut self, name: &str, f: &DateFormatFunc) -> Result<()> {
16099        self.write_keyword(name);
16100        self.write("(");
16101        self.generate_expression(&f.this)?;
16102        self.write(", ");
16103        self.generate_expression(&f.format)?;
16104        self.write(")");
16105        Ok(())
16106    }
16107
16108    fn generate_from_unixtime(&mut self, f: &FromUnixtimeFunc) -> Result<()> {
16109        self.write_keyword("FROM_UNIXTIME");
16110        self.write("(");
16111        self.generate_expression(&f.this)?;
16112        if let Some(ref format) = f.format {
16113            self.write(", ");
16114            self.generate_expression(format)?;
16115        }
16116        self.write(")");
16117        Ok(())
16118    }
16119
16120    fn generate_unix_timestamp(&mut self, f: &UnixTimestampFunc) -> Result<()> {
16121        self.write_keyword("UNIX_TIMESTAMP");
16122        self.write("(");
16123        if let Some(ref expr) = f.this {
16124            self.generate_expression(expr)?;
16125            if let Some(ref format) = f.format {
16126                self.write(", ");
16127                self.generate_expression(format)?;
16128            }
16129        } else if matches!(
16130            self.config.dialect,
16131            Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)
16132        ) {
16133            // Spark/Hive: UNIX_TIMESTAMP() -> UNIX_TIMESTAMP(CURRENT_TIMESTAMP())
16134            self.write_keyword("CURRENT_TIMESTAMP");
16135            self.write("()");
16136        }
16137        self.write(")");
16138        Ok(())
16139    }
16140
16141    fn generate_make_date(&mut self, f: &MakeDateFunc) -> Result<()> {
16142        self.write_keyword("MAKE_DATE");
16143        self.write("(");
16144        self.generate_expression(&f.year)?;
16145        self.write(", ");
16146        self.generate_expression(&f.month)?;
16147        self.write(", ");
16148        self.generate_expression(&f.day)?;
16149        self.write(")");
16150        Ok(())
16151    }
16152
16153    fn generate_make_timestamp(&mut self, f: &MakeTimestampFunc) -> Result<()> {
16154        self.write_keyword("MAKE_TIMESTAMP");
16155        self.write("(");
16156        self.generate_expression(&f.year)?;
16157        self.write(", ");
16158        self.generate_expression(&f.month)?;
16159        self.write(", ");
16160        self.generate_expression(&f.day)?;
16161        self.write(", ");
16162        self.generate_expression(&f.hour)?;
16163        self.write(", ");
16164        self.generate_expression(&f.minute)?;
16165        self.write(", ");
16166        self.generate_expression(&f.second)?;
16167        if let Some(ref tz) = f.timezone {
16168            self.write(", ");
16169            self.generate_expression(tz)?;
16170        }
16171        self.write(")");
16172        Ok(())
16173    }
16174
16175    /// Extract field names from a struct expression (either Struct or Function named STRUCT with Alias args)
16176    fn extract_struct_field_names(expr: &Expression) -> Option<Vec<String>> {
16177        match expr {
16178            Expression::Struct(s) => {
16179                if s.fields.iter().all(|(name, _)| name.is_some()) {
16180                    Some(s.fields.iter().map(|(name, _)| name.as_deref().unwrap_or("").to_string()).collect())
16181                } else {
16182                    None
16183                }
16184            }
16185            Expression::Function(f) if f.name.to_uppercase() == "STRUCT" => {
16186                // Check if all args are Alias (named fields)
16187                if f.args.iter().all(|a| matches!(a, Expression::Alias(_))) {
16188                    Some(f.args.iter().filter_map(|a| {
16189                        if let Expression::Alias(alias) = a {
16190                            Some(alias.alias.name.clone())
16191                        } else {
16192                            None
16193                        }
16194                    }).collect())
16195                } else {
16196                    None
16197                }
16198            }
16199            _ => None,
16200        }
16201    }
16202
16203    /// Check if a struct expression has any unnamed fields
16204    fn struct_has_unnamed_fields(expr: &Expression) -> bool {
16205        match expr {
16206            Expression::Struct(s) => s.fields.iter().any(|(name, _)| name.is_none()),
16207            Expression::Function(f) if f.name.to_uppercase() == "STRUCT" => {
16208                f.args.iter().any(|a| !matches!(a, Expression::Alias(_)))
16209            }
16210            _ => false,
16211        }
16212    }
16213
16214    /// Get the field count of a struct expression
16215    fn struct_field_count(expr: &Expression) -> usize {
16216        match expr {
16217            Expression::Struct(s) => s.fields.len(),
16218            Expression::Function(f) if f.name.to_uppercase() == "STRUCT" => f.args.len(),
16219            _ => 0,
16220        }
16221    }
16222
16223    /// Apply field names to an unnamed struct expression, producing a new expression with names
16224    fn apply_struct_field_names(expr: &Expression, field_names: &[String]) -> Expression {
16225        match expr {
16226            Expression::Struct(s) => {
16227                let mut new_fields = Vec::with_capacity(s.fields.len());
16228                for (i, (name, value)) in s.fields.iter().enumerate() {
16229                    if name.is_none() && i < field_names.len() {
16230                        new_fields.push((Some(field_names[i].clone()), value.clone()));
16231                    } else {
16232                        new_fields.push((name.clone(), value.clone()));
16233                    }
16234                }
16235                Expression::Struct(Box::new(crate::expressions::Struct { fields: new_fields }))
16236            }
16237            Expression::Function(f) if f.name.to_uppercase() == "STRUCT" => {
16238                let mut new_args = Vec::with_capacity(f.args.len());
16239                for (i, arg) in f.args.iter().enumerate() {
16240                    if !matches!(arg, Expression::Alias(_)) && i < field_names.len() {
16241                        // Wrap the value in an Alias with the inherited name
16242                        new_args.push(Expression::Alias(Box::new(crate::expressions::Alias {
16243                            this: arg.clone(),
16244                            alias: crate::expressions::Identifier::new(field_names[i].clone()),
16245                            column_aliases: Vec::new(),
16246                            pre_alias_comments: Vec::new(),
16247                            trailing_comments: Vec::new(),
16248                        })));
16249                    } else {
16250                        new_args.push(arg.clone());
16251                    }
16252                }
16253                Expression::Function(Box::new(crate::expressions::Function {
16254                    name: f.name.clone(),
16255                    args: new_args,
16256                    distinct: f.distinct,
16257                    trailing_comments: f.trailing_comments.clone(),
16258                    use_bracket_syntax: f.use_bracket_syntax,
16259                    no_parens: f.no_parens,
16260                    quoted: f.quoted,
16261                }))
16262            }
16263            _ => expr.clone(),
16264        }
16265    }
16266
16267    /// Propagate struct field names from the first struct in an array to subsequent unnamed structs.
16268    /// This implements BigQuery's implicit field name inheritance for struct arrays.
16269    /// Handles both Expression::Struct and Expression::Function named "STRUCT".
16270    fn inherit_struct_field_names(expressions: &[Expression]) -> Vec<Expression> {
16271        let first = match expressions.first() {
16272            Some(e) => e,
16273            None => return expressions.to_vec(),
16274        };
16275
16276        let field_names = match Self::extract_struct_field_names(first) {
16277            Some(names) if !names.is_empty() => names,
16278            _ => return expressions.to_vec(),
16279        };
16280
16281        let mut result = Vec::with_capacity(expressions.len());
16282        for (idx, expr) in expressions.iter().enumerate() {
16283            if idx == 0 {
16284                result.push(expr.clone());
16285                continue;
16286            }
16287            // Check if this is a struct with unnamed fields that needs name propagation
16288            if Self::struct_field_count(expr) == field_names.len() && Self::struct_has_unnamed_fields(expr) {
16289                result.push(Self::apply_struct_field_names(expr, &field_names));
16290            } else {
16291                result.push(expr.clone());
16292            }
16293        }
16294        result
16295    }
16296
16297    // Array function generators
16298
16299    fn generate_array_constructor(&mut self, f: &ArrayConstructor) -> Result<()> {
16300        // Apply struct name inheritance for target dialects that need it
16301        // (DuckDB, Spark, Databricks, Hive, Snowflake, Presto, Trino)
16302        let needs_inheritance = matches!(self.config.dialect,
16303            Some(DialectType::DuckDB) | Some(DialectType::Spark)
16304            | Some(DialectType::Databricks) | Some(DialectType::Hive)
16305            | Some(DialectType::Snowflake) | Some(DialectType::Presto) | Some(DialectType::Trino)
16306        );
16307        let propagated: Vec<Expression>;
16308        let expressions = if needs_inheritance && f.expressions.len() > 1 {
16309            propagated = Self::inherit_struct_field_names(&f.expressions);
16310            &propagated
16311        } else {
16312            &f.expressions
16313        };
16314
16315        // Check if elements should be split onto multiple lines (pretty + too wide)
16316        let should_split = if self.config.pretty && !expressions.is_empty() {
16317            let mut expr_strings: Vec<String> = Vec::with_capacity(expressions.len());
16318            for expr in expressions {
16319                let mut temp_gen = Generator::with_config(self.config.clone());
16320                temp_gen.config.pretty = false;
16321                temp_gen.generate_expression(expr)?;
16322                expr_strings.push(temp_gen.output);
16323            }
16324            self.too_wide(&expr_strings)
16325        } else {
16326            false
16327        };
16328
16329        if f.bracket_notation {
16330            // For Spark/Databricks, use ARRAY(...) with parens
16331            // For Presto/Trino/PostgreSQL, use ARRAY[...] with keyword prefix
16332            // For others (DuckDB, Snowflake), use bare [...]
16333            let (open, close) = match self.config.dialect {
16334                Some(DialectType::Spark) | Some(DialectType::Databricks)
16335                | Some(DialectType::Hive) => {
16336                    self.write_keyword("ARRAY");
16337                    ("(", ")")
16338                }
16339                Some(DialectType::Presto) | Some(DialectType::Trino)
16340                | Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)
16341                | Some(DialectType::Materialize) | Some(DialectType::RisingWave)
16342                | Some(DialectType::CockroachDB) => {
16343                    self.write_keyword("ARRAY");
16344                    ("[", "]")
16345                }
16346                _ => ("[", "]"),
16347            };
16348            self.write(open);
16349            if should_split {
16350                self.write_newline();
16351                self.indent_level += 1;
16352                for (i, expr) in expressions.iter().enumerate() {
16353                    self.write_indent();
16354                    self.generate_expression(expr)?;
16355                    if i + 1 < expressions.len() {
16356                        self.write(",");
16357                    }
16358                    self.write_newline();
16359                }
16360                self.indent_level -= 1;
16361                self.write_indent();
16362            } else {
16363                for (i, expr) in expressions.iter().enumerate() {
16364                    if i > 0 { self.write(", "); }
16365                    self.generate_expression(expr)?;
16366                }
16367            }
16368            self.write(close);
16369        } else {
16370            // Use LIST keyword if that was the original syntax (DuckDB)
16371            if f.use_list_keyword {
16372                self.write_keyword("LIST");
16373            } else {
16374                self.write_keyword("ARRAY");
16375            }
16376            // For Spark/Hive, always use ARRAY(...) with parens
16377            let (open, close) = if matches!(self.config.dialect,
16378                Some(DialectType::Spark)
16379                | Some(DialectType::Databricks) | Some(DialectType::Hive)) {
16380                ("(", ")")
16381            } else {
16382                ("[", "]")
16383            };
16384            self.write(open);
16385            if should_split {
16386                self.write_newline();
16387                self.indent_level += 1;
16388                for (i, expr) in expressions.iter().enumerate() {
16389                    self.write_indent();
16390                    self.generate_expression(expr)?;
16391                    if i + 1 < expressions.len() {
16392                        self.write(",");
16393                    }
16394                    self.write_newline();
16395                }
16396                self.indent_level -= 1;
16397                self.write_indent();
16398            } else {
16399                for (i, expr) in expressions.iter().enumerate() {
16400                    if i > 0 { self.write(", "); }
16401                    self.generate_expression(expr)?;
16402                }
16403            }
16404            self.write(close);
16405        }
16406        Ok(())
16407    }
16408
16409    fn generate_array_sort(&mut self, f: &ArraySortFunc) -> Result<()> {
16410        self.write_keyword("ARRAY_SORT");
16411        self.write("(");
16412        self.generate_expression(&f.this)?;
16413        if let Some(ref comp) = f.comparator {
16414            self.write(", ");
16415            self.generate_expression(comp)?;
16416        }
16417        self.write(")");
16418        Ok(())
16419    }
16420
16421    fn generate_array_join(&mut self, name: &str, f: &ArrayJoinFunc) -> Result<()> {
16422        self.write_keyword(name);
16423        self.write("(");
16424        self.generate_expression(&f.this)?;
16425        self.write(", ");
16426        self.generate_expression(&f.separator)?;
16427        if let Some(ref null_rep) = f.null_replacement {
16428            self.write(", ");
16429            self.generate_expression(null_rep)?;
16430        }
16431        self.write(")");
16432        Ok(())
16433    }
16434
16435    fn generate_unnest(&mut self, f: &UnnestFunc) -> Result<()> {
16436        self.write_keyword("UNNEST");
16437        self.write("(");
16438        self.generate_expression(&f.this)?;
16439        for extra in &f.expressions {
16440            self.write(", ");
16441            self.generate_expression(extra)?;
16442        }
16443        self.write(")");
16444        if f.with_ordinality {
16445            self.write_space();
16446            if self.config.unnest_with_ordinality {
16447                // Presto/Trino: UNNEST(arr) WITH ORDINALITY [AS alias]
16448                self.write_keyword("WITH ORDINALITY");
16449            } else if f.offset_alias.is_some() {
16450                // BigQuery: UNNEST(arr) [AS col] WITH OFFSET AS pos
16451                // Alias (if any) comes BEFORE WITH OFFSET
16452                if let Some(ref alias) = f.alias {
16453                    self.write_keyword("AS");
16454                    self.write_space();
16455                    self.generate_identifier(alias)?;
16456                    self.write_space();
16457                }
16458                self.write_keyword("WITH OFFSET");
16459                if let Some(ref offset_alias) = f.offset_alias {
16460                    self.write_space();
16461                    self.write_keyword("AS");
16462                    self.write_space();
16463                    self.generate_identifier(offset_alias)?;
16464                }
16465            } else {
16466                // WITH OFFSET (BigQuery identity) - add default "AS offset" if no explicit alias
16467                self.write_keyword("WITH OFFSET");
16468                if f.alias.is_none() {
16469                    self.write(" AS offset");
16470                }
16471            }
16472        }
16473        if let Some(ref alias) = f.alias {
16474            // Add alias for: non-WITH-OFFSET cases, Presto/Trino WITH ORDINALITY, or BigQuery WITH OFFSET + alias (no offset_alias)
16475            let should_add_alias = if !f.with_ordinality {
16476                true
16477            } else if self.config.unnest_with_ordinality {
16478                // Presto/Trino: alias comes after WITH ORDINALITY
16479                true
16480            } else if f.offset_alias.is_some() {
16481                // BigQuery expansion: alias already handled above
16482                false
16483            } else {
16484                // BigQuery WITH OFFSET + alias but no offset_alias: alias comes after
16485                true
16486            };
16487            if should_add_alias {
16488                self.write_space();
16489                self.write_keyword("AS");
16490                self.write_space();
16491                self.generate_identifier(alias)?;
16492            }
16493        }
16494        Ok(())
16495    }
16496
16497    fn generate_array_filter(&mut self, f: &ArrayFilterFunc) -> Result<()> {
16498        self.write_keyword("FILTER");
16499        self.write("(");
16500        self.generate_expression(&f.this)?;
16501        self.write(", ");
16502        self.generate_expression(&f.filter)?;
16503        self.write(")");
16504        Ok(())
16505    }
16506
16507    fn generate_array_transform(&mut self, f: &ArrayTransformFunc) -> Result<()> {
16508        self.write_keyword("TRANSFORM");
16509        self.write("(");
16510        self.generate_expression(&f.this)?;
16511        self.write(", ");
16512        self.generate_expression(&f.transform)?;
16513        self.write(")");
16514        Ok(())
16515    }
16516
16517    fn generate_sequence(&mut self, name: &str, f: &SequenceFunc) -> Result<()> {
16518        self.write_keyword(name);
16519        self.write("(");
16520        self.generate_expression(&f.start)?;
16521        self.write(", ");
16522        self.generate_expression(&f.stop)?;
16523        if let Some(ref step) = f.step {
16524            self.write(", ");
16525            self.generate_expression(step)?;
16526        }
16527        self.write(")");
16528        Ok(())
16529    }
16530
16531    // Struct function generators
16532
16533    fn generate_struct_constructor(&mut self, f: &StructConstructor) -> Result<()> {
16534        self.write_keyword("STRUCT");
16535        self.write("(");
16536        for (i, (name, expr)) in f.fields.iter().enumerate() {
16537            if i > 0 { self.write(", "); }
16538            if let Some(ref id) = name {
16539                self.generate_identifier(id)?;
16540                self.write(" ");
16541                self.write_keyword("AS");
16542                self.write(" ");
16543            }
16544            self.generate_expression(expr)?;
16545        }
16546        self.write(")");
16547        Ok(())
16548    }
16549
16550    /// Convert BigQuery STRUCT function (parsed as Function with Alias args) to target dialect
16551    fn generate_struct_function_cross_dialect(&mut self, func: &Function) -> Result<()> {
16552        // Extract named/unnamed fields from function args
16553        // Args are either Alias(this=value, alias=name) for named or plain expressions for unnamed
16554        let mut names: Vec<Option<String>> = Vec::new();
16555        let mut values: Vec<&Expression> = Vec::new();
16556        let mut all_named = true;
16557
16558        for arg in &func.args {
16559            match arg {
16560                Expression::Alias(a) => {
16561                    names.push(Some(a.alias.name.clone()));
16562                    values.push(&a.this);
16563                }
16564                _ => {
16565                    names.push(None);
16566                    values.push(arg);
16567                    all_named = false;
16568                }
16569            }
16570        }
16571
16572        if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
16573            // DuckDB: {'name': value, ...} for named, {'_0': value, ...} for unnamed
16574            self.write("{");
16575            for (i, (name, value)) in names.iter().zip(values.iter()).enumerate() {
16576                if i > 0 { self.write(", "); }
16577                if let Some(n) = name {
16578                    self.write("'");
16579                    self.write(n);
16580                    self.write("'");
16581                } else {
16582                    self.write("'_");
16583                    self.write(&i.to_string());
16584                    self.write("'");
16585                }
16586                self.write(": ");
16587                self.generate_expression(value)?;
16588            }
16589            self.write("}");
16590            return Ok(());
16591        }
16592
16593        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
16594            // Snowflake: OBJECT_CONSTRUCT('name', value, ...)
16595            self.write_keyword("OBJECT_CONSTRUCT");
16596            self.write("(");
16597            for (i, (name, value)) in names.iter().zip(values.iter()).enumerate() {
16598                if i > 0 { self.write(", "); }
16599                if let Some(n) = name {
16600                    self.write("'");
16601                    self.write(n);
16602                    self.write("'");
16603                } else {
16604                    self.write("'_");
16605                    self.write(&i.to_string());
16606                    self.write("'");
16607                }
16608                self.write(", ");
16609                self.generate_expression(value)?;
16610            }
16611            self.write(")");
16612            return Ok(());
16613        }
16614
16615        if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino)) {
16616            if all_named && !names.is_empty() {
16617                // Presto/Trino: CAST(ROW(values...) AS ROW(name TYPE, ...))
16618                // Need to infer types from values
16619                self.write_keyword("CAST");
16620                self.write("(");
16621                self.write_keyword("ROW");
16622                self.write("(");
16623                for (i, value) in values.iter().enumerate() {
16624                    if i > 0 { self.write(", "); }
16625                    self.generate_expression(value)?;
16626                }
16627                self.write(")");
16628                self.write(" ");
16629                self.write_keyword("AS");
16630                self.write(" ");
16631                self.write_keyword("ROW");
16632                self.write("(");
16633                for (i, (name, value)) in names.iter().zip(values.iter()).enumerate() {
16634                    if i > 0 { self.write(", "); }
16635                    if let Some(n) = name {
16636                        self.write(n);
16637                    }
16638                    self.write(" ");
16639                    let type_str = Self::infer_sql_type_for_presto(value);
16640                    self.write_keyword(&type_str);
16641                }
16642                self.write(")");
16643                self.write(")");
16644            } else {
16645                // Unnamed: ROW(values...)
16646                self.write_keyword("ROW");
16647                self.write("(");
16648                for (i, value) in values.iter().enumerate() {
16649                    if i > 0 { self.write(", "); }
16650                    self.generate_expression(value)?;
16651                }
16652                self.write(")");
16653            }
16654            return Ok(());
16655        }
16656
16657        // Default: ROW(values...) for other dialects
16658        self.write_keyword("ROW");
16659        self.write("(");
16660        for (i, value) in values.iter().enumerate() {
16661            if i > 0 { self.write(", "); }
16662            self.generate_expression(value)?;
16663        }
16664        self.write(")");
16665        Ok(())
16666    }
16667
16668    /// Infer SQL type name for a Presto/Trino ROW CAST from a literal expression
16669    fn infer_sql_type_for_presto(expr: &Expression) -> String {
16670        match expr {
16671            Expression::Literal(crate::expressions::Literal::String(_)) => "VARCHAR".to_string(),
16672            Expression::Literal(crate::expressions::Literal::Number(n)) => {
16673                if n.contains('.') { "DOUBLE".to_string() } else { "INTEGER".to_string() }
16674            }
16675            Expression::Boolean(_) => "BOOLEAN".to_string(),
16676            Expression::Literal(crate::expressions::Literal::Date(_)) => "DATE".to_string(),
16677            Expression::Literal(crate::expressions::Literal::Timestamp(_)) => "TIMESTAMP".to_string(),
16678            Expression::Literal(crate::expressions::Literal::Datetime(_)) => "TIMESTAMP".to_string(),
16679            Expression::Array(_) | Expression::ArrayFunc(_) => {
16680                // Try to infer element type from first element
16681                "ARRAY(VARCHAR)".to_string()
16682            }
16683            // For nested structs - generate a nested ROW type by inspecting fields
16684            Expression::Struct(_) | Expression::StructFunc(_) => "ROW".to_string(),
16685            Expression::Function(f) => {
16686                let up = f.name.to_uppercase();
16687                if up == "STRUCT" { "ROW".to_string() }
16688                else if up == "CURRENT_DATE" { "DATE".to_string() }
16689                else if up == "CURRENT_TIMESTAMP" || up == "NOW" { "TIMESTAMP".to_string() }
16690                else { "VARCHAR".to_string() }
16691            }
16692            _ => "VARCHAR".to_string(),
16693        }
16694    }
16695
16696    fn generate_struct_extract(&mut self, f: &StructExtractFunc) -> Result<()> {
16697        // DuckDB uses STRUCT_EXTRACT function syntax
16698        if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
16699            self.write_keyword("STRUCT_EXTRACT");
16700            self.write("(");
16701            self.generate_expression(&f.this)?;
16702            self.write(", ");
16703            // Output field name as string literal
16704            self.write("'");
16705            self.write(&f.field.name);
16706            self.write("'");
16707            self.write(")");
16708            return Ok(());
16709        }
16710        self.generate_expression(&f.this)?;
16711        self.write(".");
16712        self.generate_identifier(&f.field)
16713    }
16714
16715    fn generate_named_struct(&mut self, f: &NamedStructFunc) -> Result<()> {
16716        self.write_keyword("NAMED_STRUCT");
16717        self.write("(");
16718        for (i, (name, value)) in f.pairs.iter().enumerate() {
16719            if i > 0 { self.write(", "); }
16720            self.generate_expression(name)?;
16721            self.write(", ");
16722            self.generate_expression(value)?;
16723        }
16724        self.write(")");
16725        Ok(())
16726    }
16727
16728    // Map function generators
16729
16730    fn generate_map_constructor(&mut self, f: &MapConstructor) -> Result<()> {
16731        if f.curly_brace_syntax {
16732            // Curly brace syntax: MAP {'a': 1, 'b': 2} or just {'a': 1, 'b': 2}
16733            if f.with_map_keyword {
16734                self.write_keyword("MAP");
16735                self.write(" ");
16736            }
16737            self.write("{");
16738            for (i, (key, val)) in f.keys.iter().zip(f.values.iter()).enumerate() {
16739                if i > 0 { self.write(", "); }
16740                self.generate_expression(key)?;
16741                self.write(": ");
16742                self.generate_expression(val)?;
16743            }
16744            self.write("}");
16745        } else {
16746            // MAP function syntax: MAP(ARRAY[keys], ARRAY[values])
16747            self.write_keyword("MAP");
16748            self.write("(");
16749            self.write_keyword("ARRAY");
16750            self.write("[");
16751            for (i, key) in f.keys.iter().enumerate() {
16752                if i > 0 { self.write(", "); }
16753                self.generate_expression(key)?;
16754            }
16755            self.write("], ");
16756            self.write_keyword("ARRAY");
16757            self.write("[");
16758            for (i, val) in f.values.iter().enumerate() {
16759                if i > 0 { self.write(", "); }
16760                self.generate_expression(val)?;
16761            }
16762            self.write("])");
16763        }
16764        Ok(())
16765    }
16766
16767    fn generate_transform_func(&mut self, name: &str, f: &TransformFunc) -> Result<()> {
16768        self.write_keyword(name);
16769        self.write("(");
16770        self.generate_expression(&f.this)?;
16771        self.write(", ");
16772        self.generate_expression(&f.transform)?;
16773        self.write(")");
16774        Ok(())
16775    }
16776
16777    // JSON function generators
16778
16779    fn generate_json_extract(&mut self, name: &str, f: &JsonExtractFunc) -> Result<()> {
16780        use crate::dialects::DialectType;
16781
16782        // Check if we should use arrow syntax (-> or ->>)
16783        let use_arrow = f.arrow_syntax && self.dialect_supports_json_arrow();
16784
16785        if use_arrow {
16786            // Output arrow syntax: expr -> path or expr ->> path
16787            self.generate_expression(&f.this)?;
16788            if name == "JSON_EXTRACT_SCALAR" || name == "JSON_EXTRACT_PATH_TEXT" {
16789                self.write(" ->> ");
16790            } else {
16791                self.write(" -> ");
16792            }
16793            self.generate_expression(&f.path)?;
16794            return Ok(());
16795        }
16796
16797        // PostgreSQL uses #>> operator for JSONB path text extraction (only when hash_arrow_syntax is true)
16798        if f.hash_arrow_syntax && matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
16799            self.generate_expression(&f.this)?;
16800            self.write(" #>> ");
16801            self.generate_expression(&f.path)?;
16802            return Ok(());
16803        }
16804
16805        // For PostgreSQL/Redshift, use JSON_EXTRACT_PATH / JSON_EXTRACT_PATH_TEXT for extraction without arrow syntax
16806        // Redshift maps everything to JSON_EXTRACT_PATH_TEXT since it doesn't have JSON_EXTRACT_PATH
16807        let func_name = if matches!(self.config.dialect, Some(DialectType::Redshift)) {
16808            match name {
16809                "JSON_EXTRACT_SCALAR" | "JSON_EXTRACT_PATH_TEXT" | "JSON_EXTRACT" | "JSON_EXTRACT_PATH" => "JSON_EXTRACT_PATH_TEXT",
16810                _ => name,
16811            }
16812        } else if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) {
16813            match name {
16814                "JSON_EXTRACT_SCALAR" | "JSON_EXTRACT_PATH_TEXT" => "JSON_EXTRACT_PATH_TEXT",
16815                "JSON_EXTRACT" | "JSON_EXTRACT_PATH" => "JSON_EXTRACT_PATH",
16816                _ => name,
16817            }
16818        } else {
16819            name
16820        };
16821
16822        self.write_keyword(func_name);
16823        self.write("(");
16824        // For Redshift, strip CAST(... AS JSON) wrapper from the expression
16825        if matches!(self.config.dialect, Some(DialectType::Redshift)) {
16826            if let Expression::Cast(ref cast) = f.this {
16827                if matches!(cast.to, crate::expressions::DataType::Json) {
16828                    self.generate_expression(&cast.this)?;
16829                } else {
16830                    self.generate_expression(&f.this)?;
16831                }
16832            } else {
16833                self.generate_expression(&f.this)?;
16834            }
16835        } else {
16836            self.generate_expression(&f.this)?;
16837        }
16838        self.write(", ");
16839        self.generate_expression(&f.path)?;
16840
16841        // Output JSON_QUERY/JSON_VALUE options (Trino/Presto style)
16842        // These go BEFORE the closing parenthesis
16843        if let Some(ref wrapper) = f.wrapper_option {
16844            self.write_space();
16845            self.write_keyword(wrapper);
16846        }
16847        if let Some(ref quotes) = f.quotes_option {
16848            self.write_space();
16849            self.write_keyword(quotes);
16850            if f.on_scalar_string {
16851                self.write_space();
16852                self.write_keyword("ON SCALAR STRING");
16853            }
16854        }
16855        if let Some(ref on_err) = f.on_error {
16856            self.write_space();
16857            self.write_keyword(on_err);
16858        }
16859        if let Some(ref ret_type) = f.returning {
16860            self.write_space();
16861            self.write_keyword("RETURNING");
16862            self.write_space();
16863            self.generate_data_type(ret_type)?;
16864        }
16865
16866        self.write(")");
16867        Ok(())
16868    }
16869
16870    /// Check if the current dialect supports JSON arrow operators (-> and ->>)
16871    fn dialect_supports_json_arrow(&self) -> bool {
16872        use crate::dialects::DialectType;
16873        match self.config.dialect {
16874            // PostgreSQL, MySQL, DuckDB support -> and ->> operators
16875            Some(DialectType::PostgreSQL) => true,
16876            Some(DialectType::MySQL) => true,
16877            Some(DialectType::DuckDB) => true,
16878            Some(DialectType::CockroachDB) => true,
16879            Some(DialectType::StarRocks) => true,
16880            Some(DialectType::SQLite) => true,
16881            // Other dialects use function syntax
16882            _ => false,
16883        }
16884    }
16885
16886    fn generate_json_path(&mut self, name: &str, f: &JsonPathFunc) -> Result<()> {
16887        use crate::dialects::DialectType;
16888
16889        // PostgreSQL uses #> operator for JSONB path extraction
16890        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) && name == "JSON_EXTRACT_PATH" {
16891            self.generate_expression(&f.this)?;
16892            self.write(" #> ");
16893            if f.paths.len() == 1 {
16894                self.generate_expression(&f.paths[0])?;
16895            } else {
16896                // Multiple paths: ARRAY[path1, path2, ...]
16897                self.write_keyword("ARRAY");
16898                self.write("[");
16899                for (i, path) in f.paths.iter().enumerate() {
16900                    if i > 0 { self.write(", "); }
16901                    self.generate_expression(path)?;
16902                }
16903                self.write("]");
16904            }
16905            return Ok(());
16906        }
16907
16908        self.write_keyword(name);
16909        self.write("(");
16910        self.generate_expression(&f.this)?;
16911        for path in &f.paths {
16912            self.write(", ");
16913            self.generate_expression(path)?;
16914        }
16915        self.write(")");
16916        Ok(())
16917    }
16918
16919    fn generate_json_object(&mut self, f: &JsonObjectFunc) -> Result<()> {
16920        use crate::dialects::DialectType;
16921
16922        self.write_keyword("JSON_OBJECT");
16923        self.write("(");
16924        if f.star {
16925            self.write("*");
16926        } else {
16927            // BigQuery, MySQL, and SQLite use comma syntax: JSON_OBJECT('key', value)
16928            // Standard SQL uses colon syntax: JSON_OBJECT('key': value)
16929            // Also respect the json_key_value_pair_sep config
16930            let use_comma_syntax = self.config.json_key_value_pair_sep == "," ||
16931                matches!(self.config.dialect, Some(DialectType::BigQuery) | Some(DialectType::MySQL) | Some(DialectType::SQLite));
16932
16933            for (i, (key, value)) in f.pairs.iter().enumerate() {
16934                if i > 0 { self.write(", "); }
16935                self.generate_expression(key)?;
16936                if use_comma_syntax {
16937                    self.write(", ");
16938                } else {
16939                    self.write(": ");
16940                }
16941                self.generate_expression(value)?;
16942            }
16943        }
16944        if let Some(null_handling) = f.null_handling {
16945            self.write_space();
16946            match null_handling {
16947                JsonNullHandling::NullOnNull => self.write_keyword("NULL ON NULL"),
16948                JsonNullHandling::AbsentOnNull => self.write_keyword("ABSENT ON NULL"),
16949            }
16950        }
16951        if f.with_unique_keys {
16952            self.write_space();
16953            self.write_keyword("WITH UNIQUE KEYS");
16954        }
16955        if let Some(ref ret_type) = f.returning_type {
16956            self.write_space();
16957            self.write_keyword("RETURNING");
16958            self.write_space();
16959            self.generate_data_type(ret_type)?;
16960            if f.format_json {
16961                self.write_space();
16962                self.write_keyword("FORMAT JSON");
16963            }
16964            if let Some(ref enc) = f.encoding {
16965                self.write_space();
16966                self.write_keyword("ENCODING");
16967                self.write_space();
16968                self.write(enc);
16969            }
16970        }
16971        self.write(")");
16972        Ok(())
16973    }
16974
16975    fn generate_json_modify(&mut self, name: &str, f: &JsonModifyFunc) -> Result<()> {
16976        self.write_keyword(name);
16977        self.write("(");
16978        self.generate_expression(&f.this)?;
16979        for (path, value) in &f.path_values {
16980            self.write(", ");
16981            self.generate_expression(path)?;
16982            self.write(", ");
16983            self.generate_expression(value)?;
16984        }
16985        self.write(")");
16986        Ok(())
16987    }
16988
16989    fn generate_json_array_agg(&mut self, f: &JsonArrayAggFunc) -> Result<()> {
16990        self.write_keyword("JSON_ARRAYAGG");
16991        self.write("(");
16992        self.generate_expression(&f.this)?;
16993        if let Some(ref order_by) = f.order_by {
16994            self.write_space();
16995            self.write_keyword("ORDER BY");
16996            self.write_space();
16997            for (i, ord) in order_by.iter().enumerate() {
16998                if i > 0 { self.write(", "); }
16999                self.generate_ordered(ord)?;
17000            }
17001        }
17002        if let Some(null_handling) = f.null_handling {
17003            self.write_space();
17004            match null_handling {
17005                JsonNullHandling::NullOnNull => self.write_keyword("NULL ON NULL"),
17006                JsonNullHandling::AbsentOnNull => self.write_keyword("ABSENT ON NULL"),
17007            }
17008        }
17009        self.write(")");
17010        if let Some(ref filter) = f.filter {
17011            self.write_space();
17012            self.write_keyword("FILTER");
17013            self.write("(");
17014            self.write_keyword("WHERE");
17015            self.write_space();
17016            self.generate_expression(filter)?;
17017            self.write(")");
17018        }
17019        Ok(())
17020    }
17021
17022    fn generate_json_object_agg(&mut self, f: &JsonObjectAggFunc) -> Result<()> {
17023        self.write_keyword("JSON_OBJECTAGG");
17024        self.write("(");
17025        self.generate_expression(&f.key)?;
17026        self.write(": ");
17027        self.generate_expression(&f.value)?;
17028        if let Some(null_handling) = f.null_handling {
17029            self.write_space();
17030            match null_handling {
17031                JsonNullHandling::NullOnNull => self.write_keyword("NULL ON NULL"),
17032                JsonNullHandling::AbsentOnNull => self.write_keyword("ABSENT ON NULL"),
17033            }
17034        }
17035        self.write(")");
17036        if let Some(ref filter) = f.filter {
17037            self.write_space();
17038            self.write_keyword("FILTER");
17039            self.write("(");
17040            self.write_keyword("WHERE");
17041            self.write_space();
17042            self.generate_expression(filter)?;
17043            self.write(")");
17044        }
17045        Ok(())
17046    }
17047
17048    // Type casting/conversion generators
17049
17050    fn generate_convert(&mut self, f: &ConvertFunc) -> Result<()> {
17051        use crate::dialects::DialectType;
17052
17053        // Redshift: CONVERT(type, expr) -> CAST(expr AS type)
17054        if self.config.dialect == Some(DialectType::Redshift) {
17055            self.write_keyword("CAST");
17056            self.write("(");
17057            self.generate_expression(&f.this)?;
17058            self.write_space();
17059            self.write_keyword("AS");
17060            self.write_space();
17061            self.generate_data_type(&f.to)?;
17062            self.write(")");
17063            return Ok(());
17064        }
17065
17066        self.write_keyword("CONVERT");
17067        self.write("(");
17068        self.generate_data_type(&f.to)?;
17069        self.write(", ");
17070        self.generate_expression(&f.this)?;
17071        if let Some(ref style) = f.style {
17072            self.write(", ");
17073            self.generate_expression(style)?;
17074        }
17075        self.write(")");
17076        Ok(())
17077    }
17078
17079    // Additional expression generators
17080
17081    fn generate_lambda(&mut self, f: &LambdaExpr) -> Result<()> {
17082        if f.colon {
17083            // DuckDB syntax: LAMBDA x : expr
17084            self.write_keyword("LAMBDA");
17085            self.write_space();
17086            for (i, param) in f.parameters.iter().enumerate() {
17087                if i > 0 { self.write(", "); }
17088                self.generate_identifier(param)?;
17089            }
17090            self.write(" : ");
17091        } else {
17092            // Standard syntax: x -> expr or (x, y) -> expr
17093            if f.parameters.len() == 1 {
17094                self.generate_identifier(&f.parameters[0])?;
17095            } else {
17096                self.write("(");
17097                for (i, param) in f.parameters.iter().enumerate() {
17098                    if i > 0 { self.write(", "); }
17099                    self.generate_identifier(param)?;
17100                }
17101                self.write(")");
17102            }
17103            self.write(" -> ");
17104        }
17105        self.generate_expression(&f.body)
17106    }
17107
17108    fn generate_named_argument(&mut self, f: &NamedArgument) -> Result<()> {
17109        self.generate_identifier(&f.name)?;
17110        match f.separator {
17111            NamedArgSeparator::DArrow => self.write(" => "),
17112            NamedArgSeparator::ColonEq => self.write(" := "),
17113            NamedArgSeparator::Eq => self.write(" = "),
17114        }
17115        self.generate_expression(&f.value)
17116    }
17117
17118    fn generate_table_argument(&mut self, f: &TableArgument) -> Result<()> {
17119        self.write_keyword(&f.prefix);
17120        self.write(" ");
17121        self.generate_expression(&f.this)
17122    }
17123
17124    fn generate_parameter(&mut self, f: &Parameter) -> Result<()> {
17125        match f.style {
17126            ParameterStyle::Question => self.write("?"),
17127            ParameterStyle::Dollar => {
17128                self.write("$");
17129                if let Some(idx) = f.index {
17130                    self.write(&idx.to_string());
17131                } else if let Some(ref name) = f.name {
17132                    // Session variable like $x or $query_id
17133                    self.write(name);
17134                }
17135            }
17136            ParameterStyle::DollarBrace => {
17137                // Template variable like ${x} or ${hiveconf:name} (Databricks, Hive)
17138                self.write("${");
17139                if let Some(ref name) = f.name {
17140                    self.write(name);
17141                }
17142                if let Some(ref expr) = f.expression {
17143                    self.write(":");
17144                    self.write(expr);
17145                }
17146                self.write("}");
17147            }
17148            ParameterStyle::Colon => {
17149                self.write(":");
17150                if let Some(idx) = f.index {
17151                    self.write(&idx.to_string());
17152                } else if let Some(ref name) = f.name {
17153                    self.write(name);
17154                }
17155            }
17156            ParameterStyle::At => {
17157                self.write("@");
17158                if let Some(ref name) = f.name {
17159                    if f.quoted {
17160                        self.write("\"");
17161                        self.write(name);
17162                        self.write("\"");
17163                    } else {
17164                        self.write(name);
17165                    }
17166                }
17167            }
17168            ParameterStyle::DoubleAt => {
17169                self.write("@@");
17170                if let Some(ref name) = f.name {
17171                    self.write(name);
17172                }
17173            }
17174            ParameterStyle::DoubleDollar => {
17175                self.write("$$");
17176                if let Some(ref name) = f.name {
17177                    self.write(name);
17178                }
17179            }
17180            ParameterStyle::Percent => {
17181                if let Some(ref name) = f.name {
17182                    // %(name)s format
17183                    self.write("%(");
17184                    self.write(name);
17185                    self.write(")s");
17186                } else {
17187                    // %s format
17188                    self.write("%s");
17189                }
17190            }
17191            ParameterStyle::Brace => {
17192                // Spark/Databricks widget template variable: {name}
17193                // ClickHouse query parameter may include kind: {name: Type}
17194                self.write("{");
17195                if let Some(ref name) = f.name {
17196                    self.write(name);
17197                }
17198                if let Some(ref expr) = f.expression {
17199                    self.write(": ");
17200                    self.write(expr);
17201                }
17202                self.write("}");
17203            }
17204        }
17205        Ok(())
17206    }
17207
17208    fn generate_placeholder(&mut self, f: &Placeholder) -> Result<()> {
17209        self.write("?");
17210        if let Some(idx) = f.index {
17211            self.write(&idx.to_string());
17212        }
17213        Ok(())
17214    }
17215
17216    fn generate_sql_comment(&mut self, f: &SqlComment) -> Result<()> {
17217        if f.is_block {
17218            self.write("/*");
17219            self.write(&f.text);
17220            self.write("*/");
17221        } else {
17222            self.write("--");
17223            self.write(&f.text);
17224        }
17225        Ok(())
17226    }
17227
17228    // Additional predicate generators
17229
17230    fn generate_similar_to(&mut self, f: &SimilarToExpr) -> Result<()> {
17231        self.generate_expression(&f.this)?;
17232        if f.not {
17233            self.write_space();
17234            self.write_keyword("NOT");
17235        }
17236        self.write_space();
17237        self.write_keyword("SIMILAR TO");
17238        self.write_space();
17239        self.generate_expression(&f.pattern)?;
17240        if let Some(ref escape) = f.escape {
17241            self.write_space();
17242            self.write_keyword("ESCAPE");
17243            self.write_space();
17244            self.generate_expression(escape)?;
17245        }
17246        Ok(())
17247    }
17248
17249    fn generate_quantified(&mut self, name: &str, f: &QuantifiedExpr) -> Result<()> {
17250        self.generate_expression(&f.this)?;
17251        self.write_space();
17252        // Output comparison operator if present
17253        if let Some(op) = &f.op {
17254            match op {
17255                QuantifiedOp::Eq => self.write("="),
17256                QuantifiedOp::Neq => self.write("<>"),
17257                QuantifiedOp::Lt => self.write("<"),
17258                QuantifiedOp::Lte => self.write("<="),
17259                QuantifiedOp::Gt => self.write(">"),
17260                QuantifiedOp::Gte => self.write(">="),
17261            }
17262            self.write_space();
17263        }
17264        self.write_keyword(name);
17265        if self.config.quantified_no_paren_space {
17266            self.write("(");
17267        } else {
17268            self.write(" (");
17269        }
17270        self.generate_expression(&f.subquery)?;
17271        self.write(")");
17272        Ok(())
17273    }
17274
17275    fn generate_overlaps(&mut self, f: &OverlapsExpr) -> Result<()> {
17276        // Check if this is a simple binary form (this OVERLAPS expression)
17277        if let (Some(this), Some(expr)) = (&f.this, &f.expression) {
17278            self.generate_expression(this)?;
17279            self.write_space();
17280            self.write_keyword("OVERLAPS");
17281            self.write_space();
17282            self.generate_expression(expr)?;
17283        } else if let (Some(ls), Some(le), Some(rs), Some(re)) =
17284            (&f.left_start, &f.left_end, &f.right_start, &f.right_end)
17285        {
17286            // Full ANSI form: (a, b) OVERLAPS (c, d)
17287            self.write("(");
17288            self.generate_expression(ls)?;
17289            self.write(", ");
17290            self.generate_expression(le)?;
17291            self.write(")");
17292            self.write_space();
17293            self.write_keyword("OVERLAPS");
17294            self.write_space();
17295            self.write("(");
17296            self.generate_expression(rs)?;
17297            self.write(", ");
17298            self.generate_expression(re)?;
17299            self.write(")");
17300        }
17301        Ok(())
17302    }
17303
17304    // Type conversion generators
17305
17306    fn generate_try_cast(&mut self, cast: &Cast) -> Result<()> {
17307        use crate::dialects::DialectType;
17308
17309        // SingleStore uses !:> syntax for try cast
17310        if matches!(self.config.dialect, Some(DialectType::SingleStore)) {
17311            self.generate_expression(&cast.this)?;
17312            self.write(" !:> ");
17313            self.generate_data_type(&cast.to)?;
17314            return Ok(());
17315        }
17316
17317        // Teradata uses TRYCAST (no underscore)
17318        if matches!(self.config.dialect, Some(DialectType::Teradata)) {
17319            self.write_keyword("TRYCAST");
17320            self.write("(");
17321            self.generate_expression(&cast.this)?;
17322            self.write_space();
17323            self.write_keyword("AS");
17324            self.write_space();
17325            self.generate_data_type(&cast.to)?;
17326            self.write(")");
17327            return Ok(());
17328        }
17329
17330        // Dialects without TRY_CAST: generate as regular CAST
17331        let keyword = if matches!(self.config.dialect,
17332            Some(DialectType::Hive)
17333            | Some(DialectType::MySQL) | Some(DialectType::SQLite) | Some(DialectType::Oracle)
17334            | Some(DialectType::ClickHouse)
17335            | Some(DialectType::Redshift) | Some(DialectType::PostgreSQL)
17336            | Some(DialectType::StarRocks) | Some(DialectType::Doris)
17337        ) {
17338            "CAST"
17339        } else {
17340            "TRY_CAST"
17341        };
17342
17343        self.write_keyword(keyword);
17344        self.write("(");
17345        self.generate_expression(&cast.this)?;
17346        self.write_space();
17347        self.write_keyword("AS");
17348        self.write_space();
17349        self.generate_data_type(&cast.to)?;
17350
17351        // Output FORMAT clause if present
17352        if let Some(format) = &cast.format {
17353            self.write_space();
17354            self.write_keyword("FORMAT");
17355            self.write_space();
17356            self.generate_expression(format)?;
17357        }
17358
17359        self.write(")");
17360        Ok(())
17361    }
17362
17363    fn generate_safe_cast(&mut self, cast: &Cast) -> Result<()> {
17364        self.write_keyword("SAFE_CAST");
17365        self.write("(");
17366        self.generate_expression(&cast.this)?;
17367        self.write_space();
17368        self.write_keyword("AS");
17369        self.write_space();
17370        self.generate_data_type(&cast.to)?;
17371
17372        // Output FORMAT clause if present
17373        if let Some(format) = &cast.format {
17374            self.write_space();
17375            self.write_keyword("FORMAT");
17376            self.write_space();
17377            self.generate_expression(format)?;
17378        }
17379
17380        self.write(")");
17381        Ok(())
17382    }
17383
17384    // Array/struct/map access generators
17385
17386    fn generate_subscript(&mut self, s: &Subscript) -> Result<()> {
17387        self.generate_expression(&s.this)?;
17388        self.write("[");
17389        self.generate_expression(&s.index)?;
17390        self.write("]");
17391        Ok(())
17392    }
17393
17394    fn generate_dot_access(&mut self, d: &DotAccess) -> Result<()> {
17395        self.generate_expression(&d.this)?;
17396        // Snowflake uses : (colon) for first-level struct/object field access on CAST/column expressions
17397        // e.g., CAST(col AS OBJECT(fld1 OBJECT(fld2 INT))):fld1.fld2
17398        let use_colon = matches!(self.config.dialect, Some(DialectType::Snowflake))
17399            && matches!(&d.this, Expression::Cast(_) | Expression::SafeCast(_) | Expression::TryCast(_));
17400        if use_colon {
17401            self.write(":");
17402        } else {
17403            self.write(".");
17404        }
17405        self.generate_identifier(&d.field)
17406    }
17407
17408    fn generate_method_call(&mut self, m: &MethodCall) -> Result<()> {
17409        self.generate_expression(&m.this)?;
17410        self.write(".");
17411        // Method names after a dot should not be quoted based on reserved keywords
17412        // Only quote if explicitly marked as quoted in the AST
17413        if m.method.quoted {
17414            let q = self.config.identifier_quote;
17415            self.write(&format!("{}{}{}", q, m.method.name, q));
17416        } else {
17417            self.write(&m.method.name);
17418        }
17419        self.write("(");
17420        for (i, arg) in m.args.iter().enumerate() {
17421            if i > 0 {
17422                self.write(", ");
17423            }
17424            self.generate_expression(arg)?;
17425        }
17426        self.write(")");
17427        Ok(())
17428    }
17429
17430    fn generate_array_slice(&mut self, s: &ArraySlice) -> Result<()> {
17431        // Check if we need to wrap the inner expression in parentheses
17432        // JSON arrow expressions have lower precedence than array subscript
17433        let needs_parens = matches!(
17434            &s.this,
17435            Expression::JsonExtract(f) if f.arrow_syntax
17436        ) || matches!(
17437            &s.this,
17438            Expression::JsonExtractScalar(f) if f.arrow_syntax
17439        );
17440
17441        if needs_parens {
17442            self.write("(");
17443        }
17444        self.generate_expression(&s.this)?;
17445        if needs_parens {
17446            self.write(")");
17447        }
17448        self.write("[");
17449        if let Some(start) = &s.start {
17450            self.generate_expression(start)?;
17451        }
17452        self.write(":");
17453        if let Some(end) = &s.end {
17454            self.generate_expression(end)?;
17455        }
17456        self.write("]");
17457        Ok(())
17458    }
17459
17460
17461    fn generate_binary_op(&mut self, op: &BinaryOp, operator: &str) -> Result<()> {
17462        // Generate left expression, but skip trailing comments if they're already in left_comments
17463        // to avoid duplication (comments are captured as both expr.trailing_comments
17464        // and BinaryOp.left_comments during parsing)
17465        match &op.left {
17466            Expression::Column(col) => {
17467                // Generate column without trailing comments but include join_mark
17468                if let Some(table) = &col.table {
17469                    self.generate_identifier(table)?;
17470                    self.write(".");
17471                }
17472                self.generate_identifier(&col.name)?;
17473                // Oracle-style join marker (+)
17474                if col.join_mark && self.config.supports_column_join_marks {
17475                    self.write(" (+)");
17476                }
17477            }
17478            Expression::Add(inner_op) | Expression::Sub(inner_op) |
17479            Expression::Mul(inner_op) | Expression::Div(inner_op) |
17480            Expression::Concat(inner_op) => {
17481                // Generate binary op without its trailing comments
17482                self.generate_binary_op_no_trailing(inner_op, match &op.left {
17483                    Expression::Add(_) => "+",
17484                    Expression::Sub(_) => "-",
17485                    Expression::Mul(_) => "*",
17486                    Expression::Div(_) => "/",
17487                    Expression::Concat(_) => "||",
17488                    _ => unreachable!("op.left variant already matched by outer arm as Add/Sub/Mul/Div/Concat"),
17489                })?;
17490            }
17491            _ => {
17492                self.generate_expression(&op.left)?;
17493            }
17494        }
17495        // Output comments after left operand
17496        for comment in &op.left_comments {
17497            self.write_space();
17498            self.write(comment);
17499        }
17500        if self.config.pretty
17501            && matches!(self.config.dialect, Some(DialectType::Snowflake))
17502            && (operator == "AND" || operator == "OR")
17503        {
17504            self.write_newline();
17505            self.write_indent();
17506            self.write_keyword(operator);
17507        } else {
17508            self.write_space();
17509            if operator.chars().all(|c| c.is_alphabetic()) {
17510                self.write_keyword(operator);
17511            } else {
17512                self.write(operator);
17513            }
17514        }
17515        // Output comments after operator (before right operand)
17516        for comment in &op.operator_comments {
17517            self.write_space();
17518            self.write(comment);
17519        }
17520        self.write_space();
17521        self.generate_expression(&op.right)?;
17522        // Output trailing comments after right operand
17523        for comment in &op.trailing_comments {
17524            self.write_space();
17525            self.write(comment);
17526        }
17527        Ok(())
17528    }
17529
17530    /// Generate LIKE/ILIKE operation with optional ESCAPE clause
17531    fn generate_like_op(&mut self, op: &LikeOp, operator: &str) -> Result<()> {
17532        self.generate_expression(&op.left)?;
17533        self.write_space();
17534        self.write_keyword(operator);
17535        if let Some(quantifier) = &op.quantifier {
17536            self.write_space();
17537            self.write_keyword(quantifier);
17538        }
17539        self.write_space();
17540        self.generate_expression(&op.right)?;
17541        if let Some(escape) = &op.escape {
17542            self.write_space();
17543            self.write_keyword("ESCAPE");
17544            self.write_space();
17545            self.generate_expression(escape)?;
17546        }
17547        Ok(())
17548    }
17549
17550    /// Generate null-safe equality
17551    /// MySQL uses <=>, other dialects use IS NOT DISTINCT FROM
17552    fn generate_null_safe_eq(&mut self, op: &BinaryOp) -> Result<()> {
17553        use crate::dialects::DialectType;
17554        self.generate_expression(&op.left)?;
17555        self.write_space();
17556        if matches!(self.config.dialect, Some(DialectType::MySQL)) {
17557            self.write("<=>");
17558        } else {
17559            self.write_keyword("IS NOT DISTINCT FROM");
17560        }
17561        self.write_space();
17562        self.generate_expression(&op.right)?;
17563        Ok(())
17564    }
17565
17566    /// Generate IS DISTINCT FROM (null-safe inequality)
17567    fn generate_null_safe_neq(&mut self, op: &BinaryOp) -> Result<()> {
17568        self.generate_expression(&op.left)?;
17569        self.write_space();
17570        self.write_keyword("IS DISTINCT FROM");
17571        self.write_space();
17572        self.generate_expression(&op.right)?;
17573        Ok(())
17574    }
17575
17576    /// Generate binary op without trailing comments (used when nested inside another binary op)
17577    fn generate_binary_op_no_trailing(&mut self, op: &BinaryOp, operator: &str) -> Result<()> {
17578        // Generate left expression, but skip trailing comments
17579        match &op.left {
17580            Expression::Column(col) => {
17581                if let Some(table) = &col.table {
17582                    self.generate_identifier(table)?;
17583                    self.write(".");
17584                }
17585                self.generate_identifier(&col.name)?;
17586                // Oracle-style join marker (+)
17587                if col.join_mark && self.config.supports_column_join_marks {
17588                    self.write(" (+)");
17589                }
17590            }
17591            Expression::Add(inner_op) | Expression::Sub(inner_op) |
17592            Expression::Mul(inner_op) | Expression::Div(inner_op) |
17593            Expression::Concat(inner_op) => {
17594                self.generate_binary_op_no_trailing(inner_op, match &op.left {
17595                    Expression::Add(_) => "+",
17596                    Expression::Sub(_) => "-",
17597                    Expression::Mul(_) => "*",
17598                    Expression::Div(_) => "/",
17599                    Expression::Concat(_) => "||",
17600                    _ => unreachable!("op.left variant already matched by outer arm as Add/Sub/Mul/Div/Concat"),
17601                })?;
17602            }
17603            _ => {
17604                self.generate_expression(&op.left)?;
17605            }
17606        }
17607        // Output left_comments
17608        for comment in &op.left_comments {
17609            self.write_space();
17610            self.write(comment);
17611        }
17612        self.write_space();
17613        if operator.chars().all(|c| c.is_alphabetic()) {
17614            self.write_keyword(operator);
17615        } else {
17616            self.write(operator);
17617        }
17618        // Output operator_comments
17619        for comment in &op.operator_comments {
17620            self.write_space();
17621            self.write(comment);
17622        }
17623        self.write_space();
17624        // Generate right expression, but skip trailing comments if it's a Column
17625        // (the parent's left_comments will output them)
17626        match &op.right {
17627            Expression::Column(col) => {
17628                if let Some(table) = &col.table {
17629                    self.generate_identifier(table)?;
17630                    self.write(".");
17631                }
17632                self.generate_identifier(&col.name)?;
17633                // Oracle-style join marker (+)
17634                if col.join_mark && self.config.supports_column_join_marks {
17635                    self.write(" (+)");
17636                }
17637            }
17638            _ => {
17639                self.generate_expression(&op.right)?;
17640            }
17641        }
17642        // Skip trailing_comments - parent will handle them via its left_comments
17643        Ok(())
17644    }
17645
17646    fn generate_unary_op(&mut self, op: &UnaryOp, operator: &str) -> Result<()> {
17647        if operator.chars().all(|c| c.is_alphabetic()) {
17648            self.write_keyword(operator);
17649            self.write_space();
17650        } else {
17651            self.write(operator);
17652            // Add space between consecutive unary operators (e.g., "- -5" not "--5")
17653            if matches!(&op.this, Expression::Neg(_) | Expression::BitwiseNot(_)) {
17654                self.write_space();
17655            }
17656        }
17657        self.generate_expression(&op.this)
17658    }
17659
17660    fn generate_in(&mut self, in_expr: &In) -> Result<()> {
17661        self.generate_expression(&in_expr.this)?;
17662        if in_expr.global {
17663            self.write_space();
17664            self.write_keyword("GLOBAL");
17665        }
17666        if in_expr.not {
17667            self.write_space();
17668            self.write_keyword("NOT");
17669        }
17670        self.write_space();
17671        self.write_keyword("IN");
17672
17673        // BigQuery: IN UNNEST(expr)
17674        if let Some(unnest_expr) = &in_expr.unnest {
17675            self.write_space();
17676            self.write_keyword("UNNEST");
17677            self.write("(");
17678            self.generate_expression(unnest_expr)?;
17679            self.write(")");
17680            return Ok(());
17681        }
17682
17683        if let Some(query) = &in_expr.query {
17684            // Check if this is a bare identifier (PIVOT FOR foo IN y_enum)
17685            // vs a subquery (col IN (SELECT ...))
17686            let is_bare = in_expr.expressions.is_empty() && !matches!(
17687                query,
17688                Expression::Select(_) | Expression::Union(_) |
17689                Expression::Intersect(_) | Expression::Except(_) |
17690                Expression::Subquery(_)
17691            );
17692            if is_bare {
17693                // Bare identifier: no parentheses
17694                self.write_space();
17695                self.generate_expression(query)?;
17696            } else {
17697                // Subquery: with parentheses
17698                self.write(" (");
17699                let is_statement = matches!(
17700                    query,
17701                    Expression::Select(_) | Expression::Union(_) |
17702                    Expression::Intersect(_) | Expression::Except(_) |
17703                    Expression::Subquery(_)
17704                );
17705                if self.config.pretty && is_statement {
17706                    self.write_newline();
17707                    self.indent_level += 1;
17708                    self.write_indent();
17709                }
17710                self.generate_expression(query)?;
17711                if self.config.pretty && is_statement {
17712                    self.write_newline();
17713                    self.indent_level -= 1;
17714                    self.write_indent();
17715                }
17716                self.write(")");
17717            }
17718        } else {
17719            // DuckDB: IN without parentheses for single expression that is NOT a literal
17720            // (array/list membership like 'red' IN tbl.flags)
17721            // ClickHouse: IN without parentheses for single non-array expressions
17722            let is_duckdb = matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB));
17723            let is_clickhouse = matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse));
17724            let single_expr = in_expr.expressions.len() == 1;
17725            if is_clickhouse && single_expr {
17726                if let Expression::Array(arr) = &in_expr.expressions[0] {
17727                    // ClickHouse: x IN [1, 2] -> x IN (1, 2)
17728                    self.write(" (");
17729                    for (i, expr) in arr.expressions.iter().enumerate() {
17730                        if i > 0 {
17731                            self.write(", ");
17732                        }
17733                        self.generate_expression(expr)?;
17734                    }
17735                    self.write(")");
17736                } else {
17737                    self.write_space();
17738                    self.generate_expression(&in_expr.expressions[0])?;
17739                }
17740            } else {
17741                let is_bare_ref = single_expr && matches!(
17742                    &in_expr.expressions[0],
17743                    Expression::Column(_) | Expression::Identifier(_) | Expression::Dot(_)
17744                );
17745                if is_duckdb && is_bare_ref {
17746                    self.write_space();
17747                    self.generate_expression(&in_expr.expressions[0])?;
17748                } else {
17749                    // Standard IN (list)
17750                    self.write(" (");
17751                    for (i, expr) in in_expr.expressions.iter().enumerate() {
17752                        if i > 0 {
17753                            self.write(", ");
17754                        }
17755                        self.generate_expression(expr)?;
17756                    }
17757                    self.write(")");
17758                }
17759            }
17760        }
17761
17762        Ok(())
17763    }
17764
17765    fn generate_between(&mut self, between: &Between) -> Result<()> {
17766        self.generate_expression(&between.this)?;
17767        if between.not {
17768            self.write_space();
17769            self.write_keyword("NOT");
17770        }
17771        self.write_space();
17772        self.write_keyword("BETWEEN");
17773        self.write_space();
17774        self.generate_expression(&between.low)?;
17775        self.write_space();
17776        self.write_keyword("AND");
17777        self.write_space();
17778        self.generate_expression(&between.high)
17779    }
17780
17781    fn generate_is_null(&mut self, is_null: &IsNull) -> Result<()> {
17782        // Python sqlglot normalizes NOTNULL postfix to NOT x IS NULL
17783        if is_null.not && is_null.postfix_form {
17784            // NOTNULL → NOT x IS NULL
17785            self.write_keyword("NOT");
17786            self.write_space();
17787            self.generate_expression(&is_null.this)?;
17788            self.write_space();
17789            self.write_keyword("IS");
17790            self.write_space();
17791            self.write_keyword("NULL");
17792        } else {
17793            self.generate_expression(&is_null.this)?;
17794            self.write_space();
17795            self.write_keyword("IS");
17796            if is_null.not {
17797                self.write_space();
17798                self.write_keyword("NOT");
17799            }
17800            self.write_space();
17801            self.write_keyword("NULL");
17802        }
17803        Ok(())
17804    }
17805
17806    fn generate_is_true(&mut self, is_true: &IsTrueFalse) -> Result<()> {
17807        self.generate_expression(&is_true.this)?;
17808        self.write_space();
17809        self.write_keyword("IS");
17810        if is_true.not {
17811            self.write_space();
17812            self.write_keyword("NOT");
17813        }
17814        self.write_space();
17815        self.write_keyword("TRUE");
17816        Ok(())
17817    }
17818
17819    fn generate_is_false(&mut self, is_false: &IsTrueFalse) -> Result<()> {
17820        self.generate_expression(&is_false.this)?;
17821        self.write_space();
17822        self.write_keyword("IS");
17823        if is_false.not {
17824            self.write_space();
17825            self.write_keyword("NOT");
17826        }
17827        self.write_space();
17828        self.write_keyword("FALSE");
17829        Ok(())
17830    }
17831
17832    fn generate_is_json(&mut self, is_json: &IsJson) -> Result<()> {
17833        self.generate_expression(&is_json.this)?;
17834        self.write_space();
17835        self.write_keyword("IS");
17836        if is_json.negated {
17837            self.write_space();
17838            self.write_keyword("NOT");
17839        }
17840        self.write_space();
17841        self.write_keyword("JSON");
17842
17843        // Output JSON type if specified (VALUE, SCALAR, OBJECT, ARRAY)
17844        if let Some(ref json_type) = is_json.json_type {
17845            self.write_space();
17846            self.write_keyword(json_type);
17847        }
17848
17849        // Output key uniqueness constraint if specified
17850        match &is_json.unique_keys {
17851            Some(JsonUniqueKeys::With) => {
17852                self.write_space();
17853                self.write_keyword("WITH UNIQUE KEYS");
17854            }
17855            Some(JsonUniqueKeys::Without) => {
17856                self.write_space();
17857                self.write_keyword("WITHOUT UNIQUE KEYS");
17858            }
17859            Some(JsonUniqueKeys::Shorthand) => {
17860                self.write_space();
17861                self.write_keyword("UNIQUE KEYS");
17862            }
17863            None => {}
17864        }
17865
17866        Ok(())
17867    }
17868
17869    fn generate_is(&mut self, is_expr: &BinaryOp) -> Result<()> {
17870        self.generate_expression(&is_expr.left)?;
17871        self.write_space();
17872        self.write_keyword("IS");
17873        self.write_space();
17874        self.generate_expression(&is_expr.right)
17875    }
17876
17877    fn generate_exists(&mut self, exists: &Exists) -> Result<()> {
17878        if exists.not {
17879            self.write_keyword("NOT");
17880            self.write_space();
17881        }
17882        self.write_keyword("EXISTS");
17883        self.write("(");
17884        self.generate_expression(&exists.this)?;
17885        self.write(")");
17886        Ok(())
17887    }
17888
17889    fn generate_member_of(&mut self, op: &BinaryOp) -> Result<()> {
17890        self.generate_expression(&op.left)?;
17891        self.write_space();
17892        self.write_keyword("MEMBER OF");
17893        self.write("(");
17894        self.generate_expression(&op.right)?;
17895        self.write(")");
17896        Ok(())
17897    }
17898
17899    fn generate_subquery(&mut self, subquery: &Subquery) -> Result<()> {
17900        if subquery.lateral {
17901            self.write_keyword("LATERAL");
17902            self.write_space();
17903        }
17904
17905        // If the inner expression is a Paren, don't add extra parentheses
17906        // This handles cases like ((SELECT 1)) LIMIT 1 where we wrap Paren in Subquery
17907        // to carry the LIMIT modifier without adding more parens
17908        let skip_outer_parens = matches!(&subquery.this, Expression::Paren(_));
17909
17910        // Check if inner expression is a statement for pretty formatting
17911        let is_statement = matches!(
17912            &subquery.this,
17913            Expression::Select(_) | Expression::Union(_) |
17914            Expression::Intersect(_) | Expression::Except(_) |
17915            Expression::Merge(_)
17916        );
17917
17918        if !skip_outer_parens {
17919            self.write("(");
17920            if self.config.pretty && is_statement {
17921                self.write_newline();
17922                self.indent_level += 1;
17923                self.write_indent();
17924            }
17925        }
17926        self.generate_expression(&subquery.this)?;
17927
17928        // Generate ORDER BY, LIMIT, OFFSET based on modifiers_inside flag
17929        if subquery.modifiers_inside {
17930            // Generate modifiers INSIDE the parentheses: (SELECT ... LIMIT 1)
17931            if let Some(order_by) = &subquery.order_by {
17932                self.write_space();
17933                self.write_keyword("ORDER BY");
17934                self.write_space();
17935                for (i, ord) in order_by.expressions.iter().enumerate() {
17936                    if i > 0 {
17937                        self.write(", ");
17938                    }
17939                    self.generate_ordered(ord)?;
17940                }
17941            }
17942
17943            if let Some(limit) = &subquery.limit {
17944                self.write_space();
17945                self.write_keyword("LIMIT");
17946                self.write_space();
17947                self.generate_expression(&limit.this)?;
17948                if limit.percent {
17949                    self.write_space();
17950                    self.write_keyword("PERCENT");
17951                }
17952            }
17953
17954            if let Some(offset) = &subquery.offset {
17955                self.write_space();
17956                self.write_keyword("OFFSET");
17957                self.write_space();
17958                self.generate_expression(&offset.this)?;
17959            }
17960        }
17961
17962        if !skip_outer_parens {
17963            if self.config.pretty && is_statement {
17964                self.write_newline();
17965                self.indent_level -= 1;
17966                self.write_indent();
17967            }
17968            self.write(")");
17969        }
17970
17971        // Generate modifiers OUTSIDE the parentheses: (SELECT ...) LIMIT 1
17972        if !subquery.modifiers_inside {
17973            if let Some(order_by) = &subquery.order_by {
17974                self.write_space();
17975                self.write_keyword("ORDER BY");
17976                self.write_space();
17977                for (i, ord) in order_by.expressions.iter().enumerate() {
17978                    if i > 0 {
17979                        self.write(", ");
17980                    }
17981                    self.generate_ordered(ord)?;
17982                }
17983            }
17984
17985            if let Some(limit) = &subquery.limit {
17986                self.write_space();
17987                self.write_keyword("LIMIT");
17988                self.write_space();
17989                self.generate_expression(&limit.this)?;
17990                if limit.percent {
17991                    self.write_space();
17992                    self.write_keyword("PERCENT");
17993                }
17994            }
17995
17996            if let Some(offset) = &subquery.offset {
17997                self.write_space();
17998                self.write_keyword("OFFSET");
17999                self.write_space();
18000                self.generate_expression(&offset.this)?;
18001            }
18002
18003            // Generate DISTRIBUTE BY (Hive/Spark)
18004            if let Some(distribute_by) = &subquery.distribute_by {
18005                self.write_space();
18006                self.write_keyword("DISTRIBUTE BY");
18007                self.write_space();
18008                for (i, expr) in distribute_by.expressions.iter().enumerate() {
18009                    if i > 0 {
18010                        self.write(", ");
18011                    }
18012                    self.generate_expression(expr)?;
18013                }
18014            }
18015
18016            // Generate SORT BY (Hive/Spark)
18017            if let Some(sort_by) = &subquery.sort_by {
18018                self.write_space();
18019                self.write_keyword("SORT BY");
18020                self.write_space();
18021                for (i, ord) in sort_by.expressions.iter().enumerate() {
18022                    if i > 0 {
18023                        self.write(", ");
18024                    }
18025                    self.generate_ordered(ord)?;
18026                }
18027            }
18028
18029            // Generate CLUSTER BY (Hive/Spark)
18030            if let Some(cluster_by) = &subquery.cluster_by {
18031                self.write_space();
18032                self.write_keyword("CLUSTER BY");
18033                self.write_space();
18034                for (i, ord) in cluster_by.expressions.iter().enumerate() {
18035                    if i > 0 {
18036                        self.write(", ");
18037                    }
18038                    self.generate_ordered(ord)?;
18039                }
18040            }
18041        }
18042
18043        if let Some(alias) = &subquery.alias {
18044            self.write_space();
18045            // Oracle doesn't use AS for subquery aliases
18046            let skip_as = matches!(self.config.dialect, Some(crate::dialects::DialectType::Oracle));
18047            if !skip_as {
18048                self.write_keyword("AS");
18049                self.write_space();
18050            }
18051            self.generate_identifier(alias)?;
18052            if !subquery.column_aliases.is_empty() {
18053                self.write("(");
18054                for (i, col) in subquery.column_aliases.iter().enumerate() {
18055                    if i > 0 {
18056                        self.write(", ");
18057                    }
18058                    self.generate_identifier(col)?;
18059                }
18060                self.write(")");
18061            }
18062        }
18063        // Output trailing comments
18064        for comment in &subquery.trailing_comments {
18065            self.write(" ");
18066            self.write(comment);
18067        }
18068        Ok(())
18069    }
18070
18071    fn generate_pivot(&mut self, pivot: &Pivot) -> Result<()> {
18072        // Generate WITH clause if present
18073        if let Some(ref with) = pivot.with {
18074            self.generate_with(with)?;
18075            self.write_space();
18076        }
18077
18078        let direction = if pivot.unpivot { "UNPIVOT" } else { "PIVOT" };
18079
18080        // Check for Redshift UNPIVOT in FROM clause:
18081        // UNPIVOT expr [AS val AT attr]
18082        // This is when unpivot=true, expressions is empty, fields is empty, and this is not Null
18083        let is_redshift_unpivot = pivot.unpivot
18084            && pivot.expressions.is_empty()
18085            && pivot.fields.is_empty()
18086            && pivot.using.is_empty()
18087            && pivot.into.is_none()
18088            && !matches!(&pivot.this, Expression::Null(_));
18089
18090        if is_redshift_unpivot {
18091            // Redshift UNPIVOT: UNPIVOT expr [AS alias]
18092            self.write_keyword("UNPIVOT");
18093            self.write_space();
18094            self.generate_expression(&pivot.this)?;
18095            // Alias - for Redshift it can be "val AT attr" format
18096            if let Some(alias) = &pivot.alias {
18097                self.write_space();
18098                self.write_keyword("AS");
18099                self.write_space();
18100                // The alias might contain " AT " for the attr part
18101                self.write(&alias.name);
18102            }
18103            return Ok(());
18104        }
18105
18106        // Check if this is a DuckDB simplified pivot (has `using` or `into`, or no `fields`)
18107        let is_simplified = !pivot.using.is_empty() || pivot.into.is_some()
18108            || (pivot.fields.is_empty() && !pivot.expressions.is_empty()
18109                && !matches!(&pivot.this, Expression::Null(_)));
18110
18111        if is_simplified {
18112            // DuckDB simplified syntax:
18113            //   PIVOT table ON cols [IN (...)] USING agg [AS alias], ... [GROUP BY ...]
18114            //   UNPIVOT table ON cols INTO NAME col VALUE col
18115            self.write_keyword(direction);
18116            self.write_space();
18117            self.generate_expression(&pivot.this)?;
18118
18119            if !pivot.expressions.is_empty() {
18120                self.write_space();
18121                self.write_keyword("ON");
18122                self.write_space();
18123                for (i, expr) in pivot.expressions.iter().enumerate() {
18124                    if i > 0 {
18125                        self.write(", ");
18126                    }
18127                    self.generate_expression(expr)?;
18128                }
18129            }
18130
18131            // INTO (for UNPIVOT)
18132            if let Some(into) = &pivot.into {
18133                self.write_space();
18134                self.write_keyword("INTO");
18135                self.write_space();
18136                self.generate_expression(into)?;
18137            }
18138
18139            // USING (for PIVOT)
18140            if !pivot.using.is_empty() {
18141                self.write_space();
18142                self.write_keyword("USING");
18143                self.write_space();
18144                for (i, expr) in pivot.using.iter().enumerate() {
18145                    if i > 0 {
18146                        self.write(", ");
18147                    }
18148                    self.generate_expression(expr)?;
18149                }
18150            }
18151
18152            // GROUP BY
18153            if let Some(group) = &pivot.group {
18154                self.write_space();
18155                self.generate_expression(group)?;
18156            }
18157        } else {
18158            // Standard syntax:
18159            //   table PIVOT(agg [AS alias], ... FOR col IN (val [AS alias], ...) [GROUP BY ...])
18160            //   table UNPIVOT(value_col FOR name_col IN (col1, col2, ...))
18161            // Only output the table expression if it's not a Null (null is used when PIVOT comes after JOIN ON)
18162            if !matches!(&pivot.this, Expression::Null(_)) {
18163                self.generate_expression(&pivot.this)?;
18164                self.write_space();
18165            }
18166            self.write_keyword(direction);
18167            self.write("(");
18168
18169            // Aggregation expressions
18170            for (i, expr) in pivot.expressions.iter().enumerate() {
18171                if i > 0 {
18172                    self.write(", ");
18173                }
18174                self.generate_expression(expr)?;
18175            }
18176
18177            // FOR...IN fields
18178            if !pivot.fields.is_empty() {
18179                if !pivot.expressions.is_empty() {
18180                    self.write_space();
18181                }
18182                self.write_keyword("FOR");
18183                self.write_space();
18184                for (i, field) in pivot.fields.iter().enumerate() {
18185                    if i > 0 {
18186                        self.write_space();
18187                    }
18188                    // field is an In expression: column IN (values)
18189                    self.generate_expression(field)?;
18190                }
18191            }
18192
18193            // DEFAULT ON NULL
18194            if let Some(default_val) = &pivot.default_on_null {
18195                self.write_space();
18196                self.write_keyword("DEFAULT ON NULL");
18197                self.write(" (");
18198                self.generate_expression(default_val)?;
18199                self.write(")");
18200            }
18201
18202            // GROUP BY inside PIVOT parens
18203            if let Some(group) = &pivot.group {
18204                self.write_space();
18205                self.generate_expression(group)?;
18206            }
18207
18208            self.write(")");
18209        }
18210
18211        // Alias
18212        if let Some(alias) = &pivot.alias {
18213            self.write_space();
18214            self.write_keyword("AS");
18215            self.write_space();
18216            self.generate_identifier(alias)?;
18217        }
18218
18219        Ok(())
18220    }
18221
18222    fn generate_unpivot(&mut self, unpivot: &Unpivot) -> Result<()> {
18223        self.generate_expression(&unpivot.this)?;
18224        self.write_space();
18225        self.write_keyword("UNPIVOT");
18226        // Output INCLUDE NULLS or EXCLUDE NULLS if specified
18227        if let Some(include) = unpivot.include_nulls {
18228            self.write_space();
18229            if include {
18230                self.write_keyword("INCLUDE NULLS");
18231            } else {
18232                self.write_keyword("EXCLUDE NULLS");
18233            }
18234            self.write_space();
18235        }
18236        self.write("(");
18237        if unpivot.value_column_parenthesized {
18238            self.write("(");
18239        }
18240        self.generate_identifier(&unpivot.value_column)?;
18241        // Output additional value columns if present
18242        for extra_col in &unpivot.extra_value_columns {
18243            self.write(", ");
18244            self.generate_identifier(extra_col)?;
18245        }
18246        if unpivot.value_column_parenthesized {
18247            self.write(")");
18248        }
18249        self.write_space();
18250        self.write_keyword("FOR");
18251        self.write_space();
18252        self.generate_identifier(&unpivot.name_column)?;
18253        self.write_space();
18254        self.write_keyword("IN");
18255        self.write(" (");
18256        for (i, col) in unpivot.columns.iter().enumerate() {
18257            if i > 0 {
18258                self.write(", ");
18259            }
18260            self.generate_expression(col)?;
18261        }
18262        self.write("))");
18263        if let Some(alias) = &unpivot.alias {
18264            self.write_space();
18265            self.write_keyword("AS");
18266            self.write_space();
18267            self.generate_identifier(alias)?;
18268        }
18269        Ok(())
18270    }
18271
18272    fn generate_values(&mut self, values: &Values) -> Result<()> {
18273        self.write_keyword("VALUES");
18274        for (i, row) in values.expressions.iter().enumerate() {
18275            if i > 0 {
18276                self.write(",");
18277            }
18278            self.write(" (");
18279            for (j, expr) in row.expressions.iter().enumerate() {
18280                if j > 0 {
18281                    self.write(", ");
18282                }
18283                self.generate_expression(expr)?;
18284            }
18285            self.write(")");
18286        }
18287        if let Some(alias) = &values.alias {
18288            self.write_space();
18289            self.write_keyword("AS");
18290            self.write_space();
18291            self.generate_identifier(alias)?;
18292            if !values.column_aliases.is_empty() {
18293                self.write("(");
18294                for (i, col) in values.column_aliases.iter().enumerate() {
18295                    if i > 0 {
18296                        self.write(", ");
18297                    }
18298                    self.generate_identifier(col)?;
18299                }
18300                self.write(")");
18301            }
18302        }
18303        Ok(())
18304    }
18305
18306    fn generate_array(&mut self, arr: &Array) -> Result<()> {
18307        // Apply struct name inheritance for target dialects that need it
18308        let needs_inheritance = matches!(self.config.dialect,
18309            Some(DialectType::DuckDB) | Some(DialectType::Spark)
18310            | Some(DialectType::Databricks) | Some(DialectType::Hive)
18311            | Some(DialectType::Snowflake) | Some(DialectType::Presto) | Some(DialectType::Trino)
18312        );
18313        let propagated: Vec<Expression>;
18314        let expressions = if needs_inheritance && arr.expressions.len() > 1 {
18315            propagated = Self::inherit_struct_field_names(&arr.expressions);
18316            &propagated
18317        } else {
18318            &arr.expressions
18319        };
18320
18321        if !self.config.array_bracket_only {
18322            self.write_keyword("ARRAY");
18323        }
18324        self.write("[");
18325        for (i, expr) in expressions.iter().enumerate() {
18326            if i > 0 {
18327                self.write(", ");
18328            }
18329            self.generate_expression(expr)?;
18330        }
18331        self.write("]");
18332        Ok(())
18333    }
18334
18335    fn generate_tuple(&mut self, tuple: &Tuple) -> Result<()> {
18336        // Special case: Tuple(function/expr, TableAlias) pattern for table functions with typed aliases
18337        // Used for PostgreSQL functions like JSON_TO_RECORDSET: FUNC(args) AS alias(col1 type1, col2 type2)
18338        if tuple.expressions.len() == 2 {
18339            if let Expression::TableAlias(_) = &tuple.expressions[1] {
18340                // First element is the function/expression, second is the TableAlias
18341                self.generate_expression(&tuple.expressions[0])?;
18342                self.write_space();
18343                self.write_keyword("AS");
18344                self.write_space();
18345                self.generate_expression(&tuple.expressions[1])?;
18346                return Ok(());
18347            }
18348        }
18349
18350        // In pretty mode, format long tuples with each element on a new line
18351        if self.config.pretty && tuple.expressions.len() > 1 {
18352            self.write("(");
18353            self.write_newline();
18354            self.indent_level += 1;
18355            for (i, expr) in tuple.expressions.iter().enumerate() {
18356                if i > 0 {
18357                    self.write(",");
18358                    self.write_newline();
18359                }
18360                self.write_indent();
18361                self.generate_expression(expr)?;
18362            }
18363            self.indent_level -= 1;
18364            self.write_newline();
18365            self.write(")");
18366        } else {
18367            self.write("(");
18368            for (i, expr) in tuple.expressions.iter().enumerate() {
18369                if i > 0 {
18370                    self.write(", ");
18371                }
18372                self.generate_expression(expr)?;
18373            }
18374            self.write(")");
18375        }
18376        Ok(())
18377    }
18378
18379    fn generate_pipe_operator(&mut self, pipe: &PipeOperator) -> Result<()> {
18380        self.generate_expression(&pipe.this)?;
18381        self.write(" |> ");
18382        self.generate_expression(&pipe.expression)?;
18383        Ok(())
18384    }
18385
18386    fn generate_ordered(&mut self, ordered: &Ordered) -> Result<()> {
18387        self.generate_expression(&ordered.this)?;
18388        if ordered.desc {
18389            self.write_space();
18390            self.write_keyword("DESC");
18391        } else if ordered.explicit_asc {
18392            self.write_space();
18393            self.write_keyword("ASC");
18394        }
18395        if let Some(nulls_first) = ordered.nulls_first {
18396            // Determine if we should skip outputting NULLS FIRST/LAST when it's the default
18397            // for the dialect. Different dialects have different NULL ordering defaults:
18398            //
18399            // nulls_are_large (Oracle, Postgres, Snowflake, etc.):
18400            //   - ASC: NULLS LAST is default (omit NULLS LAST for ASC)
18401            //   - DESC: NULLS FIRST is default (omit NULLS FIRST for DESC)
18402            //
18403            // nulls_are_small (Spark, Hive, BigQuery, most others):
18404            //   - ASC: NULLS FIRST is default
18405            //   - DESC: NULLS LAST is default
18406            //
18407            // nulls_are_last (DuckDB, Presto, Trino, Dremio, etc.):
18408            //   - NULLS LAST is always the default regardless of sort direction
18409            let is_asc = !ordered.desc;
18410            let is_nulls_are_large = matches!(
18411                self.config.dialect,
18412                Some(DialectType::Oracle) | Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)
18413                | Some(DialectType::Snowflake)
18414            );
18415            let is_nulls_are_last = matches!(
18416                self.config.dialect,
18417                Some(DialectType::Dremio) | Some(DialectType::DuckDB) | Some(DialectType::Presto)
18418                | Some(DialectType::Trino) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
18419                | Some(DialectType::Drill) | Some(DialectType::Exasol)
18420            );
18421
18422            // Check if the NULLS ordering matches the default for this dialect
18423            let is_default_nulls = if is_nulls_are_large {
18424                // For nulls_are_large: ASC + NULLS LAST or DESC + NULLS FIRST is default
18425                (is_asc && !nulls_first) || (!is_asc && nulls_first)
18426            } else if is_nulls_are_last {
18427                // For nulls_are_last: NULLS LAST is always default
18428                !nulls_first
18429            } else {
18430                false
18431            };
18432
18433            if !is_default_nulls {
18434                self.write_space();
18435                self.write_keyword("NULLS");
18436                self.write_space();
18437                self.write_keyword(if nulls_first { "FIRST" } else { "LAST" });
18438            }
18439        }
18440        // WITH FILL clause (ClickHouse)
18441        if let Some(ref with_fill) = ordered.with_fill {
18442            self.write_space();
18443            self.generate_with_fill(with_fill)?;
18444        }
18445        Ok(())
18446    }
18447
18448    fn generate_data_type(&mut self, dt: &DataType) -> Result<()> {
18449        use crate::dialects::DialectType;
18450
18451        match dt {
18452            DataType::Boolean => {
18453                // Dialect-specific boolean type mappings
18454                match self.config.dialect {
18455                    Some(DialectType::TSQL) => self.write_keyword("BIT"),
18456                    Some(DialectType::MySQL) => self.write_keyword("BOOLEAN"), // alias for TINYINT(1)
18457                    Some(DialectType::Oracle) => {
18458                        // Oracle 23c+ supports BOOLEAN, older versions use NUMBER(1)
18459                        self.write_keyword("NUMBER(1)")
18460                    }
18461                    Some(DialectType::ClickHouse) => self.write("Bool"), // ClickHouse uses Bool (case-sensitive)
18462                    _ => self.write_keyword("BOOLEAN"),
18463                }
18464            }
18465            DataType::TinyInt { length } => {
18466                // PostgreSQL, Oracle, and Exasol don't have TINYINT, use SMALLINT
18467                // Dremio maps TINYINT to INT
18468                match self.config.dialect {
18469                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) | Some(DialectType::Oracle) | Some(DialectType::Exasol) => {
18470                        self.write_keyword("SMALLINT");
18471                    }
18472                    Some(DialectType::Teradata) => {
18473                        // Teradata uses BYTEINT for smallest integer
18474                        self.write_keyword("BYTEINT");
18475                    }
18476                    Some(DialectType::Dremio) => {
18477                        // Dremio maps TINYINT to INT
18478                        self.write_keyword("INT");
18479                    }
18480                    _ => {
18481                        self.write_keyword("TINYINT");
18482                    }
18483                }
18484                if let Some(n) = length {
18485                    if !matches!(self.config.dialect, Some(DialectType::Dremio)) {
18486                        self.write(&format!("({})", n));
18487                    }
18488                }
18489            }
18490            DataType::SmallInt { length } => {
18491                // Dremio maps SMALLINT to INT, SQLite maps SMALLINT to INTEGER
18492                match self.config.dialect {
18493                    Some(DialectType::Dremio) => {
18494                        self.write_keyword("INT");
18495                    }
18496                    Some(DialectType::SQLite) => {
18497                        self.write_keyword("INTEGER");
18498                    }
18499                    _ => {
18500                        self.write_keyword("SMALLINT");
18501                        if let Some(n) = length {
18502                            self.write(&format!("({})", n));
18503                        }
18504                    }
18505                }
18506            }
18507            DataType::Int { length, integer_spelling } => {
18508                // BigQuery uses INT64 for INT
18509                if matches!(self.config.dialect, Some(DialectType::BigQuery)) {
18510                    self.write_keyword("INT64");
18511                } else if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
18512                    self.write("Int32");
18513                } else {
18514                    // TSQL, Presto, Trino, SQLite, Redshift use INTEGER as the canonical form
18515                    let use_integer = match self.config.dialect {
18516                        Some(DialectType::TSQL) | Some(DialectType::Fabric)
18517                        | Some(DialectType::Presto) | Some(DialectType::Trino)
18518                        | Some(DialectType::SQLite) | Some(DialectType::Redshift) => true,
18519                        // Databricks preserves the original spelling
18520                        Some(DialectType::Databricks) => *integer_spelling,
18521                        _ => false,
18522                    };
18523                    if use_integer {
18524                        self.write_keyword("INTEGER");
18525                    } else {
18526                        self.write_keyword("INT");
18527                    }
18528                    if let Some(n) = length {
18529                        self.write(&format!("({})", n));
18530                    }
18531                }
18532            }
18533            DataType::BigInt { length } => {
18534                // Dialect-specific bigint type mappings
18535                match self.config.dialect {
18536                    Some(DialectType::Oracle) => {
18537                        // Oracle doesn't have BIGINT, uses NUMBER or INT
18538                        self.write_keyword("NUMBER(19)");
18539                    }
18540                    _ => {
18541                        self.write_keyword("BIGINT");
18542                        if let Some(n) = length {
18543                            self.write(&format!("({})", n));
18544                        }
18545                    }
18546                }
18547            }
18548            DataType::Float { precision, scale, real_spelling } => {
18549                // Dialect-specific float type mappings
18550                // If real_spelling is true, preserve REAL; otherwise use dialect default
18551                // Spark/Hive don't support REAL, always use FLOAT
18552                if *real_spelling && !matches!(self.config.dialect, Some(DialectType::Spark)
18553                    | Some(DialectType::Databricks) | Some(DialectType::Hive)
18554                    | Some(DialectType::Snowflake) | Some(DialectType::MySQL)
18555                    | Some(DialectType::BigQuery)) {
18556                    self.write_keyword("REAL")
18557                } else {
18558                    match self.config.dialect {
18559                        Some(DialectType::PostgreSQL) => {
18560                            self.write_keyword("REAL")
18561                        }
18562                        Some(DialectType::BigQuery) => {
18563                            self.write_keyword("FLOAT64")
18564                        }
18565                        _ => self.write_keyword("FLOAT"),
18566                    }
18567                }
18568                // MySQL supports FLOAT(precision) or FLOAT(precision, scale)
18569                // Spark/Hive don't support FLOAT(precision)
18570                if !matches!(self.config.dialect, Some(DialectType::Spark)
18571                    | Some(DialectType::Databricks) | Some(DialectType::Hive)
18572                    | Some(DialectType::Presto) | Some(DialectType::Trino)) {
18573                    if let Some(p) = precision {
18574                        self.write(&format!("({}", p));
18575                        if let Some(s) = scale {
18576                            self.write(&format!(", {})", s));
18577                        } else {
18578                            self.write(")");
18579                        }
18580                    }
18581                }
18582            }
18583            DataType::Double { precision, scale } => {
18584                // Dialect-specific double type mappings
18585                match self.config.dialect {
18586                    Some(DialectType::TSQL) | Some(DialectType::Fabric) => self.write_keyword("FLOAT"), // SQL Server/Fabric FLOAT is double
18587                    Some(DialectType::Oracle) => self.write_keyword("DOUBLE PRECISION"),
18588                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) | Some(DialectType::Teradata) => {
18589                        self.write_keyword("DOUBLE PRECISION")
18590                    }
18591                    _ => self.write_keyword("DOUBLE"),
18592                }
18593                // MySQL supports DOUBLE(precision, scale)
18594                if let Some(p) = precision {
18595                    self.write(&format!("({}", p));
18596                    if let Some(s) = scale {
18597                        self.write(&format!(", {})", s));
18598                    } else {
18599                        self.write(")");
18600                    }
18601                }
18602            }
18603            DataType::Decimal { precision, scale } => {
18604                // Dialect-specific decimal type mappings
18605                match self.config.dialect {
18606                    Some(DialectType::ClickHouse) => {
18607                        self.write("Decimal");
18608                        if let Some(p) = precision {
18609                            self.write(&format!("({}", p));
18610                            if let Some(s) = scale {
18611                                self.write(&format!(", {}", s));
18612                            }
18613                            self.write(")");
18614                        }
18615                    }
18616                    Some(DialectType::Oracle) => {
18617                        // Oracle uses NUMBER instead of DECIMAL
18618                        self.write_keyword("NUMBER");
18619                        if let Some(p) = precision {
18620                            self.write(&format!("({}", p));
18621                            if let Some(s) = scale {
18622                                self.write(&format!(", {}", s));
18623                            }
18624                            self.write(")");
18625                        }
18626                    }
18627                    Some(DialectType::BigQuery) => {
18628                        // BigQuery uses NUMERIC instead of DECIMAL
18629                        self.write_keyword("NUMERIC");
18630                        if let Some(p) = precision {
18631                            self.write(&format!("({}", p));
18632                            if let Some(s) = scale {
18633                                self.write(&format!(", {}", s));
18634                            }
18635                            self.write(")");
18636                        }
18637                    }
18638                    _ => {
18639                        self.write_keyword("DECIMAL");
18640                        if let Some(p) = precision {
18641                            self.write(&format!("({}", p));
18642                            if let Some(s) = scale {
18643                                self.write(&format!(", {}", s));
18644                            }
18645                            self.write(")");
18646                        }
18647                    }
18648                }
18649            }
18650            DataType::Char { length } => {
18651                // Dialect-specific char type mappings
18652                match self.config.dialect {
18653                    Some(DialectType::DuckDB) => {
18654                        // DuckDB maps CHAR to TEXT
18655                        self.write_keyword("TEXT");
18656                    }
18657                    Some(DialectType::Dremio) => {
18658                        // Dremio maps CHAR to VARCHAR
18659                        self.write_keyword("VARCHAR");
18660                        if let Some(n) = length {
18661                            self.write(&format!("({})", n));
18662                        }
18663                    }
18664                    _ => {
18665                        self.write_keyword("CHAR");
18666                        if let Some(n) = length {
18667                            self.write(&format!("({})", n));
18668                        }
18669                    }
18670                }
18671            }
18672            DataType::VarChar { length, parenthesized_length } => {
18673                // Dialect-specific varchar type mappings
18674                match self.config.dialect {
18675                    Some(DialectType::Oracle) => {
18676                        self.write_keyword("VARCHAR2");
18677                        if let Some(n) = length {
18678                            self.write(&format!("({})", n));
18679                        }
18680                    }
18681                    Some(DialectType::DuckDB) => {
18682                        // DuckDB maps VARCHAR to TEXT
18683                        self.write_keyword("TEXT");
18684                    }
18685                    Some(DialectType::SQLite) => {
18686                        // SQLite maps VARCHAR to TEXT, preserving length
18687                        self.write_keyword("TEXT");
18688                        if let Some(n) = length {
18689                            self.write(&format!("({})", n));
18690                        }
18691                    }
18692                    Some(DialectType::MySQL) if length.is_none() => {
18693                        // MySQL requires VARCHAR to have a size - if it doesn't, use TEXT
18694                        self.write_keyword("TEXT");
18695                    }
18696                    Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks) if length.is_none() => {
18697                        // Hive/Spark/Databricks: VARCHAR without length → STRING
18698                        self.write_keyword("STRING");
18699                    }
18700                    _ => {
18701                        self.write_keyword("VARCHAR");
18702                        if let Some(n) = length {
18703                            // Hive uses VARCHAR((n)) with extra parentheses in STRUCT definitions
18704                            if *parenthesized_length {
18705                                self.write(&format!("(({}))", n));
18706                            } else {
18707                                self.write(&format!("({})", n));
18708                            }
18709                        }
18710                    }
18711                }
18712            }
18713            DataType::Text => {
18714                // Dialect-specific text type mappings
18715                match self.config.dialect {
18716                    Some(DialectType::Oracle) => self.write_keyword("CLOB"),
18717                    Some(DialectType::TSQL) => self.write_keyword("NVARCHAR(MAX)"),
18718                    Some(DialectType::BigQuery) => self.write_keyword("STRING"),
18719                    Some(DialectType::Snowflake) | Some(DialectType::Dremio) => self.write_keyword("VARCHAR"),
18720                    Some(DialectType::Exasol) => self.write_keyword("LONG VARCHAR"),
18721                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARCHAR"),
18722                    Some(DialectType::Spark) | Some(DialectType::Databricks)
18723                    | Some(DialectType::Hive) => self.write_keyword("STRING"),
18724                    Some(DialectType::Redshift) => self.write_keyword("VARCHAR"),
18725                    Some(DialectType::StarRocks) => self.write_keyword("STRING"),
18726                    _ => self.write_keyword("TEXT"),
18727                }
18728            }
18729            DataType::String { length } => {
18730                // STRING type with optional length (BigQuery STRING(n))
18731                match self.config.dialect {
18732                    Some(DialectType::ClickHouse) => {
18733                        // ClickHouse uses String with specific casing
18734                        self.write("String");
18735                        if let Some(n) = length {
18736                            self.write(&format!("({})", n));
18737                        }
18738                    }
18739                    Some(DialectType::BigQuery) => {
18740                        self.write_keyword("STRING");
18741                        if let Some(n) = length {
18742                            self.write(&format!("({})", n));
18743                        }
18744                    }
18745                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
18746                        // PostgreSQL doesn't have STRING - use VARCHAR or TEXT
18747                        if let Some(n) = length {
18748                            self.write_keyword("VARCHAR");
18749                            self.write(&format!("({})", n));
18750                        } else {
18751                            self.write_keyword("TEXT");
18752                        }
18753                    }
18754                    Some(DialectType::MySQL) | Some(DialectType::StarRocks) | Some(DialectType::Doris) => {
18755                        // MySQL doesn't have STRING - use VARCHAR or TEXT
18756                        if let Some(n) = length {
18757                            self.write_keyword("VARCHAR");
18758                            self.write(&format!("({})", n));
18759                        } else {
18760                            self.write_keyword("TEXT");
18761                        }
18762                    }
18763                    Some(DialectType::TSQL) => {
18764                        // TSQL doesn't have STRING - use VARCHAR or NVARCHAR(MAX)
18765                        if let Some(n) = length {
18766                            self.write_keyword("VARCHAR");
18767                            self.write(&format!("({})", n));
18768                        } else {
18769                            self.write_keyword("NVARCHAR(MAX)");
18770                        }
18771                    }
18772                    Some(DialectType::DuckDB) => {
18773                        // DuckDB uses TEXT for string types
18774                        self.write_keyword("TEXT");
18775                        if let Some(n) = length {
18776                            self.write(&format!("({})", n));
18777                        }
18778                    }
18779                    Some(DialectType::Presto) | Some(DialectType::Trino) => {
18780                        // Presto/Trino use VARCHAR for string types
18781                        self.write_keyword("VARCHAR");
18782                        if let Some(n) = length {
18783                            self.write(&format!("({})", n));
18784                        }
18785                    }
18786                    _ => {
18787                        // Default: output STRING with optional length
18788                        self.write_keyword("STRING");
18789                        if let Some(n) = length {
18790                            self.write(&format!("({})", n));
18791                        }
18792                    }
18793                }
18794            }
18795            DataType::Binary { length } => {
18796                // Dialect-specific binary type mappings
18797                match self.config.dialect {
18798                    Some(DialectType::PostgreSQL) => {
18799                        self.write_keyword("BYTEA");
18800                    }
18801                    Some(DialectType::Redshift) => {
18802                        self.write_keyword("VARBYTE");
18803                    }
18804                    Some(DialectType::DuckDB) => {
18805                        // DuckDB maps BINARY to BLOB
18806                        self.write_keyword("BLOB");
18807                    }
18808                    Some(DialectType::Dremio) => {
18809                        // Dremio maps BINARY to VARBINARY
18810                        self.write_keyword("VARBINARY");
18811                        if let Some(n) = length {
18812                            self.write(&format!("({})", n));
18813                        }
18814                    }
18815                    _ => {
18816                        self.write_keyword("BINARY");
18817                        if let Some(n) = length {
18818                            self.write(&format!("({})", n));
18819                        }
18820                    }
18821                }
18822            }
18823            DataType::VarBinary { length } => {
18824                // Dialect-specific varbinary type mappings
18825                match self.config.dialect {
18826                    Some(DialectType::PostgreSQL) => {
18827                        self.write_keyword("BYTEA");
18828                    }
18829                    Some(DialectType::Redshift) => {
18830                        self.write_keyword("VARBYTE");
18831                        if let Some(n) = length {
18832                            self.write(&format!("({})", n));
18833                        }
18834                    }
18835                    Some(DialectType::DuckDB) => {
18836                        // DuckDB maps VARBINARY to BLOB
18837                        self.write_keyword("BLOB");
18838                    }
18839                    Some(DialectType::Exasol) => {
18840                        // Exasol maps VARBINARY to VARCHAR
18841                        self.write_keyword("VARCHAR");
18842                    }
18843                    Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks) => {
18844                        // Spark/Hive use BINARY instead of VARBINARY
18845                        self.write_keyword("BINARY");
18846                    }
18847                    _ => {
18848                        self.write_keyword("VARBINARY");
18849                        if let Some(n) = length {
18850                            self.write(&format!("({})", n));
18851                        }
18852                    }
18853                }
18854            }
18855            DataType::Blob => {
18856                // Dialect-specific blob type mappings
18857                match self.config.dialect {
18858                    Some(DialectType::PostgreSQL) => self.write_keyword("BYTEA"),
18859                    Some(DialectType::Redshift) => self.write_keyword("VARBYTE"),
18860                    Some(DialectType::TSQL) | Some(DialectType::Fabric) => self.write_keyword("VARBINARY"),
18861                    Some(DialectType::BigQuery) => self.write_keyword("BYTES"),
18862                    Some(DialectType::Exasol) => self.write_keyword("VARCHAR"),
18863                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARBINARY"),
18864                    Some(DialectType::DuckDB) => {
18865                        // Python sqlglot: BLOB -> VARBINARY for DuckDB (base TYPE_MAPPING)
18866                        // DuckDB identity works via: BLOB -> transform VarBinary -> generator BLOB
18867                        self.write_keyword("VARBINARY");
18868                    }
18869                    Some(DialectType::Spark) | Some(DialectType::Databricks)
18870                    | Some(DialectType::Hive) => self.write_keyword("BINARY"),
18871                    Some(DialectType::ClickHouse) => self.write("Nullable(String)"),
18872                    _ => self.write_keyword("BLOB"),
18873                }
18874            }
18875            DataType::Bit { length } => {
18876                // Dialect-specific bit type mappings
18877                match self.config.dialect {
18878                    Some(DialectType::Dremio) | Some(DialectType::Spark)
18879                    | Some(DialectType::Databricks) | Some(DialectType::Hive)
18880                    | Some(DialectType::Snowflake) | Some(DialectType::BigQuery)
18881                    | Some(DialectType::Presto) | Some(DialectType::Trino)
18882                    | Some(DialectType::ClickHouse) | Some(DialectType::Redshift) => {
18883                        // These dialects don't support BIT type, use BOOLEAN
18884                        self.write_keyword("BOOLEAN");
18885                    }
18886                    _ => {
18887                        self.write_keyword("BIT");
18888                        if let Some(n) = length {
18889                            self.write(&format!("({})", n));
18890                        }
18891                    }
18892                }
18893            }
18894            DataType::VarBit { length } => {
18895                self.write_keyword("VARBIT");
18896                if let Some(n) = length {
18897                    self.write(&format!("({})", n));
18898                }
18899            }
18900            DataType::Date => self.write_keyword("DATE"),
18901            DataType::Time { precision, timezone } => {
18902                if *timezone {
18903                    // Dialect-specific TIME WITH TIME ZONE output
18904                    match self.config.dialect {
18905                        Some(DialectType::DuckDB) => {
18906                            // DuckDB: TIMETZ (drops precision)
18907                            self.write_keyword("TIMETZ");
18908                        }
18909                        Some(DialectType::PostgreSQL) => {
18910                            // PostgreSQL: TIMETZ or TIMETZ(p)
18911                            self.write_keyword("TIMETZ");
18912                            if let Some(p) = precision {
18913                                self.write(&format!("({})", p));
18914                            }
18915                        }
18916                        _ => {
18917                            // Presto/Trino/Redshift/others: TIME(p) WITH TIME ZONE
18918                            self.write_keyword("TIME");
18919                            if let Some(p) = precision {
18920                                self.write(&format!("({})", p));
18921                            }
18922                            self.write_keyword(" WITH TIME ZONE");
18923                        }
18924                    }
18925                } else {
18926                    // Spark/Hive/Databricks: TIME -> TIMESTAMP (TIME not supported)
18927                    if matches!(self.config.dialect, Some(DialectType::Spark)
18928                        | Some(DialectType::Databricks) | Some(DialectType::Hive)) {
18929                        self.write_keyword("TIMESTAMP");
18930                    } else {
18931                        self.write_keyword("TIME");
18932                        if let Some(p) = precision {
18933                            self.write(&format!("({})", p));
18934                        }
18935                    }
18936                }
18937            }
18938            DataType::Timestamp { precision, timezone } => {
18939                // Dialect-specific timestamp type mappings
18940                match self.config.dialect {
18941                    Some(DialectType::ClickHouse) => {
18942                        self.write("DateTime");
18943                        if let Some(p) = precision {
18944                            self.write(&format!("({})", p));
18945                        }
18946                    }
18947                    Some(DialectType::TSQL) => {
18948                        if *timezone {
18949                            self.write_keyword("DATETIMEOFFSET");
18950                        } else {
18951                            self.write_keyword("DATETIME2");
18952                        }
18953                        if let Some(p) = precision {
18954                            self.write(&format!("({})", p));
18955                        }
18956                    }
18957                    Some(DialectType::MySQL) => {
18958                        // MySQL uses TIMESTAMP for both cases
18959                        // MySQL's TIMESTAMP has implicit timezone behavior
18960                        self.write_keyword("TIMESTAMP");
18961                        if let Some(p) = precision {
18962                            self.write(&format!("({})", p));
18963                        }
18964                    }
18965                    Some(DialectType::BigQuery) => {
18966                        // BigQuery: TIMESTAMP is always UTC, DATETIME is timezone-naive
18967                        if *timezone {
18968                            self.write_keyword("TIMESTAMP");
18969                        } else {
18970                            self.write_keyword("DATETIME");
18971                        }
18972                    }
18973                    Some(DialectType::DuckDB) => {
18974                        // DuckDB: TIMESTAMPTZ shorthand
18975                        if *timezone {
18976                            self.write_keyword("TIMESTAMPTZ");
18977                        } else {
18978                            self.write_keyword("TIMESTAMP");
18979                            if let Some(p) = precision {
18980                                self.write(&format!("({})", p));
18981                            }
18982                        }
18983                    }
18984                    _ => {
18985                        if *timezone && !self.config.tz_to_with_time_zone {
18986                            // Use TIMESTAMPTZ shorthand when dialect doesn't prefer WITH TIME ZONE
18987                            self.write_keyword("TIMESTAMPTZ");
18988                            if let Some(p) = precision {
18989                                self.write(&format!("({})", p));
18990                            }
18991                        } else {
18992                            self.write_keyword("TIMESTAMP");
18993                            if let Some(p) = precision {
18994                                self.write(&format!("({})", p));
18995                            }
18996                            if *timezone {
18997                                self.write_space();
18998                                self.write_keyword("WITH TIME ZONE");
18999                            }
19000                        }
19001                    }
19002                }
19003            }
19004            DataType::Interval { unit, to } => {
19005                self.write_keyword("INTERVAL");
19006                if let Some(u) = unit {
19007                    self.write_space();
19008                    self.write_keyword(u);
19009                }
19010                // Handle range intervals like DAY TO HOUR
19011                if let Some(t) = to {
19012                    self.write_space();
19013                    self.write_keyword("TO");
19014                    self.write_space();
19015                    self.write_keyword(t);
19016                }
19017            }
19018            DataType::Json => {
19019                // Dialect-specific JSON type mappings
19020                match self.config.dialect {
19021                    Some(DialectType::Oracle) => self.write_keyword("JSON"), // Oracle 21c+
19022                    Some(DialectType::TSQL) => self.write_keyword("NVARCHAR(MAX)"), // No native JSON type
19023                    Some(DialectType::MySQL) => self.write_keyword("JSON"),
19024                    Some(DialectType::Snowflake) => self.write_keyword("VARIANT"),
19025                    _ => self.write_keyword("JSON"),
19026                }
19027            }
19028            DataType::JsonB => {
19029                // JSONB is PostgreSQL specific, but Doris also supports it
19030                match self.config.dialect {
19031                    Some(DialectType::PostgreSQL) => self.write_keyword("JSONB"),
19032                    Some(DialectType::Doris) => self.write_keyword("JSONB"),
19033                    Some(DialectType::Snowflake) => self.write_keyword("VARIANT"),
19034                    Some(DialectType::TSQL) => self.write_keyword("NVARCHAR(MAX)"),
19035                    Some(DialectType::DuckDB) => self.write_keyword("JSON"), // DuckDB maps JSONB to JSON
19036                    _ => self.write_keyword("JSON"), // Fall back to JSON for other dialects
19037                }
19038            }
19039            DataType::Uuid => {
19040                // Dialect-specific UUID type mappings
19041                match self.config.dialect {
19042                    Some(DialectType::TSQL) => self.write_keyword("UNIQUEIDENTIFIER"),
19043                    Some(DialectType::MySQL) => self.write_keyword("CHAR(36)"),
19044                    Some(DialectType::Oracle) => self.write_keyword("RAW(16)"),
19045                    Some(DialectType::BigQuery) | Some(DialectType::Spark) | Some(DialectType::Databricks) => self.write_keyword("STRING"),
19046                    _ => self.write_keyword("UUID"),
19047                }
19048            }
19049            DataType::Array { element_type, dimension } => {
19050                // Dialect-specific array syntax
19051                match self.config.dialect {
19052                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) | Some(DialectType::DuckDB) => {
19053                        // PostgreSQL uses TYPE[] or TYPE[N] syntax
19054                        self.generate_data_type(element_type)?;
19055                        if let Some(dim) = dimension {
19056                            self.write(&format!("[{}]", dim));
19057                        } else {
19058                            self.write("[]");
19059                        }
19060                    }
19061                    Some(DialectType::BigQuery) => {
19062                        self.write_keyword("ARRAY<");
19063                        self.generate_data_type(element_type)?;
19064                        self.write(">");
19065                    }
19066                    Some(DialectType::Snowflake) | Some(DialectType::Presto) | Some(DialectType::Trino)
19067                    | Some(DialectType::ClickHouse) => {
19068                        // These dialects use Array(TYPE) parentheses syntax
19069                        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
19070                            self.write("Array(");
19071                        } else {
19072                            self.write_keyword("ARRAY(");
19073                        }
19074                        self.generate_data_type(element_type)?;
19075                        self.write(")");
19076                    }
19077                    Some(DialectType::TSQL) | Some(DialectType::MySQL) | Some(DialectType::Oracle) => {
19078                        // These dialects don't have native array types
19079                        // Fall back to JSON or use native workarounds
19080                        match self.config.dialect {
19081                            Some(DialectType::MySQL) => self.write_keyword("JSON"),
19082                            Some(DialectType::TSQL) => self.write_keyword("NVARCHAR(MAX)"),
19083                            _ => self.write_keyword("JSON"),
19084                        }
19085                    }
19086                    _ => {
19087                        // Default: use angle bracket syntax (ARRAY<T>)
19088                        self.write_keyword("ARRAY<");
19089                        self.generate_data_type(element_type)?;
19090                        self.write(">");
19091                    }
19092                }
19093            }
19094            DataType::List { element_type } => {
19095                // Materialize: element_type LIST (postfix syntax)
19096                self.generate_data_type(element_type)?;
19097                self.write_keyword(" LIST");
19098            }
19099            DataType::Map { key_type, value_type } => {
19100                // Use parentheses for Snowflake and RisingWave, bracket syntax for Materialize, angle brackets for others
19101                match self.config.dialect {
19102                    Some(DialectType::Materialize) => {
19103                        // Materialize: MAP[key_type => value_type]
19104                        self.write_keyword("MAP[");
19105                        self.generate_data_type(key_type)?;
19106                        self.write(" => ");
19107                        self.generate_data_type(value_type)?;
19108                        self.write("]");
19109                    }
19110                    Some(DialectType::Snowflake) | Some(DialectType::RisingWave) | Some(DialectType::DuckDB)
19111                    | Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => {
19112                        self.write_keyword("MAP(");
19113                        self.generate_data_type(key_type)?;
19114                        self.write(", ");
19115                        self.generate_data_type(value_type)?;
19116                        self.write(")");
19117                    }
19118                    _ => {
19119                        self.write_keyword("MAP<");
19120                        self.generate_data_type(key_type)?;
19121                        self.write(", ");
19122                        self.generate_data_type(value_type)?;
19123                        self.write(">");
19124                    }
19125                }
19126            }
19127            DataType::Vector { element_type, dimension } => {
19128                if matches!(self.config.dialect, Some(DialectType::SingleStore)) {
19129                    // SingleStore format: VECTOR(dimension, type_alias)
19130                    self.write_keyword("VECTOR(");
19131                    if let Some(dim) = dimension {
19132                        self.write(&dim.to_string());
19133                    }
19134                    // Map type back to SingleStore alias
19135                    let type_alias = element_type.as_ref().and_then(|et| match et.as_ref() {
19136                        DataType::TinyInt { .. } => Some("I8"),
19137                        DataType::SmallInt { .. } => Some("I16"),
19138                        DataType::Int { .. } => Some("I32"),
19139                        DataType::BigInt { .. } => Some("I64"),
19140                        DataType::Float { .. } => Some("F32"),
19141                        DataType::Double { .. } => Some("F64"),
19142                        _ => None,
19143                    });
19144                    if let Some(alias) = type_alias {
19145                        if dimension.is_some() {
19146                            self.write(", ");
19147                        }
19148                        self.write(alias);
19149                    }
19150                    self.write(")");
19151                } else {
19152                    // Snowflake format: VECTOR(type, dimension)
19153                    self.write_keyword("VECTOR(");
19154                    if let Some(ref et) = element_type {
19155                        self.generate_data_type(et)?;
19156                        if dimension.is_some() {
19157                            self.write(", ");
19158                        }
19159                    }
19160                    if let Some(dim) = dimension {
19161                        self.write(&dim.to_string());
19162                    }
19163                    self.write(")");
19164                }
19165            }
19166            DataType::Object { fields, modifier } => {
19167                self.write_keyword("OBJECT(");
19168                for (i, (name, dt, not_null)) in fields.iter().enumerate() {
19169                    if i > 0 {
19170                        self.write(", ");
19171                    }
19172                    self.write(name);
19173                    self.write(" ");
19174                    self.generate_data_type(dt)?;
19175                    if *not_null {
19176                        self.write_keyword(" NOT NULL");
19177                    }
19178                }
19179                self.write(")");
19180                if let Some(mod_str) = modifier {
19181                    self.write(" ");
19182                    self.write_keyword(mod_str);
19183                }
19184            }
19185            DataType::Struct { fields, nested } => {
19186                // Dialect-specific struct type mappings
19187                match self.config.dialect {
19188                    Some(DialectType::Snowflake) => {
19189                        // Snowflake maps STRUCT to OBJECT
19190                        self.write_keyword("OBJECT(");
19191                        for (i, field) in fields.iter().enumerate() {
19192                            if i > 0 {
19193                                self.write(", ");
19194                            }
19195                            if !field.name.is_empty() {
19196                                self.write(&field.name);
19197                                self.write(" ");
19198                            }
19199                            self.generate_data_type(&field.data_type)?;
19200                        }
19201                        self.write(")");
19202                    }
19203                    Some(DialectType::Presto) | Some(DialectType::Trino) => {
19204                        // Presto/Trino use ROW(name TYPE, ...) syntax
19205                        self.write_keyword("ROW(");
19206                        for (i, field) in fields.iter().enumerate() {
19207                            if i > 0 {
19208                                self.write(", ");
19209                            }
19210                            if !field.name.is_empty() {
19211                                self.write(&field.name);
19212                                self.write(" ");
19213                            }
19214                            self.generate_data_type(&field.data_type)?;
19215                        }
19216                        self.write(")");
19217                    }
19218                    Some(DialectType::DuckDB) => {
19219                        // DuckDB uses parenthesized syntax: STRUCT(name TYPE, ...)
19220                        self.write_keyword("STRUCT(");
19221                        for (i, field) in fields.iter().enumerate() {
19222                            if i > 0 {
19223                                self.write(", ");
19224                            }
19225                            if !field.name.is_empty() {
19226                                self.write(&field.name);
19227                                self.write(" ");
19228                            }
19229                            self.generate_data_type(&field.data_type)?;
19230                        }
19231                        self.write(")");
19232                    }
19233                    Some(DialectType::SingleStore) => {
19234                        // SingleStore uses RECORD(name TYPE, ...) for struct types
19235                        self.write_keyword("RECORD(");
19236                        for (i, field) in fields.iter().enumerate() {
19237                            if i > 0 {
19238                                self.write(", ");
19239                            }
19240                            if !field.name.is_empty() {
19241                                self.write(&field.name);
19242                                self.write(" ");
19243                            }
19244                            self.generate_data_type(&field.data_type)?;
19245                        }
19246                        self.write(")");
19247                    }
19248                    _ => {
19249                        // Hive/Spark always use angle bracket syntax: STRUCT<name: TYPE>
19250                        let force_angle_brackets = matches!(
19251                            self.config.dialect,
19252                            Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)
19253                        );
19254                        if *nested && !force_angle_brackets {
19255                            self.write_keyword("STRUCT(");
19256                            for (i, field) in fields.iter().enumerate() {
19257                                if i > 0 {
19258                                    self.write(", ");
19259                                }
19260                                if !field.name.is_empty() {
19261                                    self.write(&field.name);
19262                                    self.write(" ");
19263                                }
19264                                self.generate_data_type(&field.data_type)?;
19265                            }
19266                            self.write(")");
19267                        } else {
19268                            self.write_keyword("STRUCT<");
19269                            for (i, field) in fields.iter().enumerate() {
19270                                if i > 0 {
19271                                    self.write(", ");
19272                                }
19273                                if !field.name.is_empty() {
19274                                    // Named field: name TYPE (with configurable separator for Hive)
19275                                    self.write(&field.name);
19276                                    self.write(self.config.struct_field_sep);
19277                                }
19278                                // For anonymous fields, just output the type
19279                                self.generate_data_type(&field.data_type)?;
19280                                // Spark/Databricks: Output COMMENT clause if present
19281                                if let Some(comment) = &field.comment {
19282                                    self.write(" COMMENT '");
19283                                    self.write(comment);
19284                                    self.write("'");
19285                                }
19286                                // BigQuery: Output OPTIONS clause if present
19287                                if !field.options.is_empty() {
19288                                    self.write(" ");
19289                                    self.generate_options_clause(&field.options)?;
19290                                }
19291                            }
19292                            self.write(">");
19293                        }
19294                    }
19295                }
19296            }
19297            DataType::Enum { values, assignments } => {
19298                // DuckDB ENUM type: ENUM('RED', 'GREEN', 'BLUE')
19299                // ClickHouse: Enum('hello' = 1, 'world' = 2)
19300                if self.config.dialect == Some(DialectType::ClickHouse) {
19301                    self.write("Enum(");
19302                } else {
19303                    self.write_keyword("ENUM(");
19304                }
19305                for (i, val) in values.iter().enumerate() {
19306                    if i > 0 {
19307                        self.write(", ");
19308                    }
19309                    self.write("'");
19310                    self.write(val);
19311                    self.write("'");
19312                    if let Some(Some(assignment)) = assignments.get(i) {
19313                        self.write(" = ");
19314                        self.write(assignment);
19315                    }
19316                }
19317                self.write(")");
19318            }
19319            DataType::Set { values } => {
19320                // MySQL SET type: SET('a', 'b', 'c')
19321                self.write_keyword("SET(");
19322                for (i, val) in values.iter().enumerate() {
19323                    if i > 0 {
19324                        self.write(", ");
19325                    }
19326                    self.write("'");
19327                    self.write(val);
19328                    self.write("'");
19329                }
19330                self.write(")");
19331            }
19332            DataType::Union { fields } => {
19333                // DuckDB UNION type: UNION(num INT, str TEXT)
19334                self.write_keyword("UNION(");
19335                for (i, (name, dt)) in fields.iter().enumerate() {
19336                    if i > 0 {
19337                        self.write(", ");
19338                    }
19339                    if !name.is_empty() {
19340                        self.write(name);
19341                        self.write(" ");
19342                    }
19343                    self.generate_data_type(dt)?;
19344                }
19345                self.write(")");
19346            }
19347            DataType::Nullable { inner } => {
19348                // ClickHouse: Nullable(T), other dialects: just the inner type
19349                if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
19350                    self.write("Nullable(");
19351                    self.generate_data_type(inner)?;
19352                    self.write(")");
19353                } else {
19354                    // Map ClickHouse-specific custom type names to standard types
19355                    match inner.as_ref() {
19356                        DataType::Custom { name } if name.to_uppercase() == "DATETIME" => {
19357                            self.generate_data_type(&DataType::Timestamp { precision: None, timezone: false })?;
19358                        }
19359                        _ => {
19360                            self.generate_data_type(inner)?;
19361                        }
19362                    }
19363                }
19364            }
19365            DataType::Custom { name } => {
19366                // Handle dialect-specific type transformations
19367                let name_upper = name.to_uppercase();
19368                match self.config.dialect {
19369                    Some(DialectType::ClickHouse) => {
19370                        let (base_upper, suffix) = if let Some(idx) = name.find('(') {
19371                            (name_upper[..idx].to_string(), &name[idx..])
19372                        } else {
19373                            (name_upper.clone(), "")
19374                        };
19375                        let mapped = match base_upper.as_str() {
19376                            "DATETIME" | "TIMESTAMPTZ" | "TIMESTAMP" | "TIMESTAMPNTZ" | "SMALLDATETIME" | "DATETIME2" => "DateTime",
19377                            "DATETIME64" => "DateTime64",
19378                            "DATE32" => "Date32",
19379                            "INT" => "Int32",
19380                            "MEDIUMINT" => "Int32",
19381                            "INT8" => "Int8",
19382                            "INT16" => "Int16",
19383                            "INT32" => "Int32",
19384                            "INT64" => "Int64",
19385                            "INT128" => "Int128",
19386                            "INT256" => "Int256",
19387                            "UINT8" => "UInt8",
19388                            "UINT16" => "UInt16",
19389                            "UINT32" => "UInt32",
19390                            "UINT64" => "UInt64",
19391                            "UINT128" => "UInt128",
19392                            "UINT256" => "UInt256",
19393                            "FLOAT32" => "Float32",
19394                            "FLOAT64" => "Float64",
19395                            "DECIMAL32" => "Decimal32",
19396                            "DECIMAL64" => "Decimal64",
19397                            "DECIMAL128" => "Decimal128",
19398                            "DECIMAL256" => "Decimal256",
19399                            "ENUM" => "Enum",
19400                            "ENUM8" => "Enum8",
19401                            "ENUM16" => "Enum16",
19402                            "FIXEDSTRING" => "FixedString",
19403                            "NESTED" => "Nested",
19404                            "LOWCARDINALITY" => "LowCardinality",
19405                            "NULLABLE" => "Nullable",
19406                            "IPV4" => "IPv4",
19407                            "IPV6" => "IPv6",
19408                            "POINT" => "Point",
19409                            "RING" => "Ring",
19410                            "LINESTRING" => "LineString",
19411                            "MULTILINESTRING" => "MultiLineString",
19412                            "POLYGON" => "Polygon",
19413                            "MULTIPOLYGON" => "MultiPolygon",
19414                            "AGGREGATEFUNCTION" => "AggregateFunction",
19415                            "SIMPLEAGGREGATEFUNCTION" => "SimpleAggregateFunction",
19416                            "DYNAMIC" => "Dynamic",
19417                            _ => "",
19418                        };
19419                        if mapped.is_empty() {
19420                            self.write(name);
19421                        } else {
19422                            self.write(mapped);
19423                            self.write(suffix);
19424                        }
19425                    }
19426                    Some(DialectType::MySQL) if name_upper == "TIMESTAMPTZ" || name_upper == "TIMESTAMPLTZ" => {
19427                        // MySQL doesn't support TIMESTAMPTZ/TIMESTAMPLTZ, use TIMESTAMP
19428                        self.write_keyword("TIMESTAMP");
19429                    }
19430                    Some(DialectType::TSQL) if name_upper == "VARIANT" => {
19431                        self.write_keyword("SQL_VARIANT");
19432                    }
19433                    Some(DialectType::DuckDB) if name_upper == "DECFLOAT" => {
19434                        self.write_keyword("DECIMAL(38, 5)");
19435                    }
19436                    Some(DialectType::Exasol) => {
19437                        // Exasol type mappings for custom types
19438                        match name_upper.as_str() {
19439                            // Binary types → VARCHAR
19440                            "LONGBLOB" | "MEDIUMBLOB" | "TINYBLOB" => self.write_keyword("VARCHAR"),
19441                            // Text types → VARCHAR (TEXT → LONG VARCHAR is handled by DataType::Text)
19442                            "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" => self.write_keyword("VARCHAR"),
19443                            // Integer types
19444                            "MEDIUMINT" => self.write_keyword("INT"),
19445                            // Decimal types → DECIMAL
19446                            "DECIMAL32" | "DECIMAL64" | "DECIMAL128" | "DECIMAL256" => self.write_keyword("DECIMAL"),
19447                            // Timestamp types
19448                            "DATETIME" => self.write_keyword("TIMESTAMP"),
19449                            "TIMESTAMPLTZ" => self.write_keyword("TIMESTAMP WITH LOCAL TIME ZONE"),
19450                            _ => self.write(name),
19451                        }
19452                    }
19453                    Some(DialectType::Dremio) => {
19454                        // Dremio type mappings for custom types
19455                        match name_upper.as_str() {
19456                            "TIMESTAMPNTZ" | "DATETIME" => self.write_keyword("TIMESTAMP"),
19457                            "ARRAY" => self.write_keyword("LIST"),
19458                            "NCHAR" => self.write_keyword("VARCHAR"),
19459                            _ => self.write(name),
19460                        }
19461                    }
19462                    // Map dialect-specific custom types to standard SQL types for other dialects
19463                    _ => {
19464                        // Extract base name and args for types with parenthesized args (e.g., DATETIME2(3))
19465                        let (base_upper, _args_str) = if let Some(idx) = name_upper.find('(') {
19466                            (name_upper[..idx].to_string(), Some(&name[idx..]))
19467                        } else {
19468                            (name_upper.clone(), None)
19469                        };
19470
19471                        match base_upper.as_str() {
19472                            "INT64" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19473                                self.write_keyword("BIGINT");
19474                            }
19475                            "FLOAT64" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19476                                self.write_keyword("DOUBLE");
19477                            }
19478                            "BOOL" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19479                                self.write_keyword("BOOLEAN");
19480                            }
19481                            "BYTES" if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)) => {
19482                                self.write_keyword("BINARY");
19483                            }
19484                            "BYTES" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19485                                self.write_keyword("VARBINARY");
19486                            }
19487                            // TSQL DATETIME2/SMALLDATETIME -> TIMESTAMP
19488                            "DATETIME2" | "SMALLDATETIME" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19489                                // PostgreSQL preserves precision, others don't
19490                                if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
19491                                    self.write_keyword("TIMESTAMP");
19492                                    if let Some(args) = _args_str {
19493                                        self.write(args);
19494                                    }
19495                                } else {
19496                                    self.write_keyword("TIMESTAMP");
19497                                }
19498                            }
19499                            // TSQL DATETIMEOFFSET -> TIMESTAMPTZ
19500                            "DATETIMEOFFSET" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19501                                if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
19502                                    self.write_keyword("TIMESTAMPTZ");
19503                                    if let Some(args) = _args_str {
19504                                        self.write(args);
19505                                    }
19506                                } else {
19507                                    self.write_keyword("TIMESTAMPTZ");
19508                                }
19509                            }
19510                            // TSQL UNIQUEIDENTIFIER -> UUID or STRING
19511                            "UNIQUEIDENTIFIER" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19512                                match self.config.dialect {
19513                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19514                                    | Some(DialectType::Hive) => self.write_keyword("STRING"),
19515                                    _ => self.write_keyword("UUID"),
19516                                }
19517                            }
19518                            // TSQL BIT -> BOOLEAN for most dialects
19519                            "BIT" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)
19520                                | Some(DialectType::PostgreSQL) | Some(DialectType::MySQL) | Some(DialectType::DuckDB)) => {
19521                                self.write_keyword("BOOLEAN");
19522                            }
19523                            // TSQL NVARCHAR -> VARCHAR (with default size 30 for some dialects)
19524                            "NVARCHAR" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19525                                match self.config.dialect {
19526                                    Some(DialectType::SQLite) => {
19527                                        self.write_keyword("TEXT");
19528                                        if let Some(args) = _args_str {
19529                                            self.write(args);
19530                                        }
19531                                    }
19532                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19533                                    | Some(DialectType::Hive) => {
19534                                        if _args_str.is_some() {
19535                                            self.write_keyword("VARCHAR");
19536                                            self.write(_args_str.unwrap());
19537                                        } else {
19538                                            self.write_keyword("VARCHAR(30)");
19539                                        }
19540                                    }
19541                                    _ => {
19542                                        self.write_keyword("VARCHAR");
19543                                        if let Some(args) = _args_str {
19544                                            self.write(args);
19545                                        }
19546                                    }
19547                                }
19548                            }
19549                            // TSQL NCHAR -> CHAR
19550                            "NCHAR" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19551                                match self.config.dialect {
19552                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19553                                    | Some(DialectType::Hive) => {
19554                                        if _args_str.is_some() {
19555                                            self.write_keyword("CHAR");
19556                                            self.write(_args_str.unwrap());
19557                                        } else {
19558                                            self.write_keyword("CHAR(30)");
19559                                        }
19560                                    }
19561                                    _ => {
19562                                        self.write_keyword("CHAR");
19563                                        if let Some(args) = _args_str {
19564                                            self.write(args);
19565                                        }
19566                                    }
19567                                }
19568                            }
19569                            // MySQL text variant types -> map to appropriate target type
19570                            // For MySQL/SingleStore: keep original name (column definitions), CAST handling is in generate_cast
19571                            "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" => {
19572                                match self.config.dialect {
19573                                    Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB) => self.write_keyword(&base_upper),
19574                                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => self.write_keyword("TEXT"),
19575                                    Some(DialectType::BigQuery) => self.write_keyword("STRING"),
19576                                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARCHAR"),
19577                                    Some(DialectType::Snowflake) | Some(DialectType::Redshift) | Some(DialectType::Dremio) => self.write_keyword("VARCHAR"),
19578                                    _ => self.write_keyword("TEXT"),
19579                                }
19580                            }
19581                            // MySQL blob variant types -> map to appropriate target type
19582                            // For MySQL/SingleStore: keep original name (column definitions), CAST handling is in generate_cast
19583                            "LONGBLOB" | "MEDIUMBLOB" | "TINYBLOB" => {
19584                                match self.config.dialect {
19585                                    Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB) => self.write_keyword(&base_upper),
19586                                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => self.write_keyword("BLOB"),
19587                                    Some(DialectType::DuckDB) => self.write_keyword("VARBINARY"),
19588                                    Some(DialectType::BigQuery) => self.write_keyword("BYTES"),
19589                                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARBINARY"),
19590                                    Some(DialectType::Snowflake) | Some(DialectType::Redshift) | Some(DialectType::Dremio) => self.write_keyword("VARBINARY"),
19591                                    _ => self.write_keyword("BLOB"),
19592                                }
19593                            }
19594                            // LONGVARCHAR -> TEXT for SQLite, VARCHAR for others
19595                            "LONGVARCHAR" => {
19596                                match self.config.dialect {
19597                                    Some(DialectType::SQLite) => self.write_keyword("TEXT"),
19598                                    _ => self.write_keyword("VARCHAR"),
19599                                }
19600                            }
19601                            _ => self.write(name),
19602                        }
19603                    }
19604                }
19605            }
19606            DataType::Geometry { subtype, srid } => {
19607                // Dialect-specific geometry type mappings
19608                match self.config.dialect {
19609                    Some(DialectType::MySQL) => {
19610                        // MySQL uses POINT SRID 4326 syntax for specific types
19611                        if let Some(sub) = subtype {
19612                            self.write_keyword(sub);
19613                            if let Some(s) = srid {
19614                                self.write(" SRID ");
19615                                self.write(&s.to_string());
19616                            }
19617                        } else {
19618                            self.write_keyword("GEOMETRY");
19619                        }
19620                    }
19621                    Some(DialectType::BigQuery) => {
19622                        // BigQuery only supports GEOGRAPHY, not GEOMETRY
19623                        self.write_keyword("GEOGRAPHY");
19624                    }
19625                    Some(DialectType::Teradata) => {
19626                        // Teradata uses ST_GEOMETRY
19627                        self.write_keyword("ST_GEOMETRY");
19628                        if subtype.is_some() || srid.is_some() {
19629                            self.write("(");
19630                            if let Some(sub) = subtype {
19631                                self.write_keyword(sub);
19632                            }
19633                            if let Some(s) = srid {
19634                                if subtype.is_some() {
19635                                    self.write(", ");
19636                                }
19637                                self.write(&s.to_string());
19638                            }
19639                            self.write(")");
19640                        }
19641                    }
19642                    _ => {
19643                        // PostgreSQL, Snowflake, DuckDB use GEOMETRY(subtype, srid) syntax
19644                        self.write_keyword("GEOMETRY");
19645                        if subtype.is_some() || srid.is_some() {
19646                            self.write("(");
19647                            if let Some(sub) = subtype {
19648                                self.write_keyword(sub);
19649                            }
19650                            if let Some(s) = srid {
19651                                if subtype.is_some() {
19652                                    self.write(", ");
19653                                }
19654                                self.write(&s.to_string());
19655                            }
19656                            self.write(")");
19657                        }
19658                    }
19659                }
19660            }
19661            DataType::Geography { subtype, srid } => {
19662                // Dialect-specific geography type mappings
19663                match self.config.dialect {
19664                    Some(DialectType::MySQL) => {
19665                        // MySQL doesn't have native GEOGRAPHY, use GEOMETRY with SRID 4326
19666                        if let Some(sub) = subtype {
19667                            self.write_keyword(sub);
19668                        } else {
19669                            self.write_keyword("GEOMETRY");
19670                        }
19671                        // Geography implies SRID 4326 (WGS84)
19672                        let effective_srid = srid.unwrap_or(4326);
19673                        self.write(" SRID ");
19674                        self.write(&effective_srid.to_string());
19675                    }
19676                    Some(DialectType::BigQuery) => {
19677                        // BigQuery uses simple GEOGRAPHY without parameters
19678                        self.write_keyword("GEOGRAPHY");
19679                    }
19680                    Some(DialectType::Snowflake) => {
19681                        // Snowflake uses GEOGRAPHY without parameters
19682                        self.write_keyword("GEOGRAPHY");
19683                    }
19684                    _ => {
19685                        // PostgreSQL uses GEOGRAPHY(subtype, srid) syntax
19686                        self.write_keyword("GEOGRAPHY");
19687                        if subtype.is_some() || srid.is_some() {
19688                            self.write("(");
19689                            if let Some(sub) = subtype {
19690                                self.write_keyword(sub);
19691                            }
19692                            if let Some(s) = srid {
19693                                if subtype.is_some() {
19694                                    self.write(", ");
19695                                }
19696                                self.write(&s.to_string());
19697                            }
19698                            self.write(")");
19699                        }
19700                    }
19701                }
19702            }
19703            DataType::CharacterSet { name } => {
19704                // For MySQL CONVERT USING - output as CHAR CHARACTER SET name
19705                self.write_keyword("CHAR CHARACTER SET ");
19706                self.write(name);
19707            }
19708            _ => self.write("UNKNOWN"),
19709        }
19710        Ok(())
19711    }
19712
19713    // === Helper methods ===
19714
19715    fn write(&mut self, s: &str) {
19716        self.output.push_str(s);
19717    }
19718
19719    fn write_space(&mut self) {
19720        self.output.push(' ');
19721    }
19722
19723    fn write_keyword(&mut self, keyword: &str) {
19724        if self.config.uppercase_keywords {
19725            self.output.push_str(keyword);
19726        } else {
19727            self.output.push_str(&keyword.to_lowercase());
19728        }
19729    }
19730
19731    /// Convert strptime format string to Exasol format string
19732    /// Exasol TIME_MAPPING (reverse of Python sqlglot):
19733    /// %Y -> YYYY, %y -> YY, %m -> MM, %d -> DD, %H -> HH, %M -> MI, %S -> SS, %a -> DY
19734    fn convert_strptime_to_exasol_format(format: &str) -> String {
19735        let mut result = String::new();
19736        let chars: Vec<char> = format.chars().collect();
19737        let mut i = 0;
19738        while i < chars.len() {
19739            if chars[i] == '%' && i + 1 < chars.len() {
19740                let spec = chars[i + 1];
19741                let exasol_spec = match spec {
19742                    'Y' => "YYYY",
19743                    'y' => "YY",
19744                    'm' => "MM",
19745                    'd' => "DD",
19746                    'H' => "HH",
19747                    'M' => "MI",
19748                    'S' => "SS",
19749                    'a' => "DY",   // abbreviated weekday name
19750                    'A' => "DAY",  // full weekday name
19751                    'b' => "MON",  // abbreviated month name
19752                    'B' => "MONTH", // full month name
19753                    'I' => "H12",  // 12-hour format
19754                    'u' => "ID",   // ISO weekday (1-7)
19755                    'V' => "IW",   // ISO week number
19756                    'G' => "IYYY", // ISO year
19757                    'W' => "UW",   // Week number (Monday as first day)
19758                    'U' => "UW",   // Week number (Sunday as first day)
19759                    'z' => "Z",    // timezone offset
19760                    _ => {
19761                        // Unknown specifier, keep as-is
19762                        result.push('%');
19763                        result.push(spec);
19764                        i += 2;
19765                        continue;
19766                    }
19767                };
19768                result.push_str(exasol_spec);
19769                i += 2;
19770            } else {
19771                result.push(chars[i]);
19772                i += 1;
19773            }
19774        }
19775        result
19776    }
19777
19778    /// Convert strptime format string to PostgreSQL/Redshift format string
19779    /// PostgreSQL INVERSE_TIME_MAPPING from Python sqlglot:
19780    /// %Y -> YYYY, %y -> YY, %m -> MM, %d -> DD, %H -> HH24, %M -> MI, %S -> SS, %f -> US, etc.
19781    fn convert_strptime_to_postgres_format(format: &str) -> String {
19782        let mut result = String::new();
19783        let chars: Vec<char> = format.chars().collect();
19784        let mut i = 0;
19785        while i < chars.len() {
19786            if chars[i] == '%' && i + 1 < chars.len() {
19787                let spec = chars[i + 1];
19788                let pg_spec = match spec {
19789                    'Y' => "YYYY",
19790                    'y' => "YY",
19791                    'm' => "MM",
19792                    'd' => "DD",
19793                    'H' => "HH24",
19794                    'I' => "HH12",
19795                    'M' => "MI",
19796                    'S' => "SS",
19797                    'f' => "US",     // microseconds
19798                    'u' => "D",      // day of week (1=Monday)
19799                    'j' => "DDD",    // day of year
19800                    'z' => "OF",     // UTC offset
19801                    'Z' => "TZ",     // timezone name
19802                    'A' => "TMDay",  // full weekday name
19803                    'a' => "TMDy",   // abbreviated weekday name
19804                    'b' => "TMMon",  // abbreviated month name
19805                    'B' => "TMMonth", // full month name
19806                    'U' => "WW",     // week number
19807                    _ => {
19808                        // Unknown specifier, keep as-is
19809                        result.push('%');
19810                        result.push(spec);
19811                        i += 2;
19812                        continue;
19813                    }
19814                };
19815                result.push_str(pg_spec);
19816                i += 2;
19817            } else {
19818                result.push(chars[i]);
19819                i += 1;
19820            }
19821        }
19822        result
19823    }
19824
19825    /// Write a LIMIT expression value, evaluating constant expressions if limit_only_literals is set
19826    fn write_limit_expr(&mut self, expr: &Expression) -> Result<()> {
19827        if self.config.limit_only_literals {
19828            if let Some(value) = Self::try_evaluate_constant(expr) {
19829                self.write(&value.to_string());
19830                return Ok(());
19831            }
19832        }
19833        self.generate_expression(expr)
19834    }
19835
19836    /// Format a comment with proper spacing.
19837    /// Converts `/*text*/` to `/* text */` (adding internal spaces if not present).
19838    /// Python SQLGlot normalizes comment format to have spaces inside block comments.
19839    fn write_formatted_comment(&mut self, comment: &str) {
19840        // Normalize all comments to block comment format /* ... */
19841        // This matches Python sqlglot behavior which always outputs block comments
19842        let content = if comment.starts_with("/*") && comment.ends_with("*/") {
19843            // Already block comment - extract inner content
19844            comment[2..comment.len() - 2].trim()
19845        } else if comment.starts_with("--") {
19846            // Line comment - extract content after --
19847            comment[2..].trim()
19848        } else {
19849            // Raw content (no delimiters)
19850            comment.trim()
19851        };
19852        self.output.push_str("/* ");
19853        self.output.push_str(content);
19854        self.output.push_str(" */");
19855    }
19856
19857    /// Escape a raw block content (from dollar-quoted string) for single-quoted output.
19858    /// Escapes single quotes with backslash, and for Snowflake also escapes backslashes.
19859    fn escape_block_for_single_quote(&self, block: &str) -> String {
19860        let escape_backslash = matches!(
19861            self.config.dialect,
19862            Some(crate::dialects::DialectType::Snowflake)
19863        );
19864        let mut escaped = String::with_capacity(block.len() + 4);
19865        for ch in block.chars() {
19866            if ch == '\'' {
19867                escaped.push('\\');
19868                escaped.push('\'');
19869            } else if escape_backslash && ch == '\\' {
19870                escaped.push('\\');
19871                escaped.push('\\');
19872            } else {
19873                escaped.push(ch);
19874            }
19875        }
19876        escaped
19877    }
19878
19879    fn write_newline(&mut self) {
19880        self.output.push('\n');
19881    }
19882
19883    fn write_indent(&mut self) {
19884        for _ in 0..self.indent_level {
19885            self.output.push_str(&self.config.indent);
19886        }
19887    }
19888
19889    // === SQLGlot-style pretty printing helpers ===
19890
19891    /// Returns the separator string for pretty printing.
19892    /// Check if the total length of arguments exceeds max_text_width.
19893    /// Used for dynamic line breaking in expressions() formatting.
19894    fn too_wide(&self, args: &[String]) -> bool {
19895        args.iter().map(|s| s.len()).sum::<usize>() > self.config.max_text_width
19896    }
19897
19898    /// Writes a clause with a single condition (WHERE, HAVING, QUALIFY).
19899    /// In pretty mode: newline + indented keyword + newline + indented condition
19900    fn write_clause_condition(&mut self, keyword: &str, condition: &Expression) -> Result<()> {
19901        if self.config.pretty {
19902            self.write_newline();
19903            self.write_indent();
19904            self.write_keyword(keyword);
19905            self.write_newline();
19906            self.indent_level += 1;
19907            self.write_indent();
19908            self.generate_expression(condition)?;
19909            self.indent_level -= 1;
19910        } else {
19911            self.write_space();
19912            self.write_keyword(keyword);
19913            self.write_space();
19914            self.generate_expression(condition)?;
19915        }
19916        Ok(())
19917    }
19918
19919    /// Writes a clause with a list of expressions (GROUP BY, DISTRIBUTE BY, CLUSTER BY).
19920    /// In pretty mode: each expression on new line with indentation
19921    fn write_clause_expressions(&mut self, keyword: &str, exprs: &[Expression]) -> Result<()> {
19922        if exprs.is_empty() {
19923            return Ok(());
19924        }
19925
19926        if self.config.pretty {
19927            self.write_newline();
19928            self.write_indent();
19929            self.write_keyword(keyword);
19930            self.write_newline();
19931            self.indent_level += 1;
19932            for (i, expr) in exprs.iter().enumerate() {
19933                if i > 0 {
19934                    self.write(",");
19935                    self.write_newline();
19936                }
19937                self.write_indent();
19938                self.generate_expression(expr)?;
19939            }
19940            self.indent_level -= 1;
19941        } else {
19942            self.write_space();
19943            self.write_keyword(keyword);
19944            self.write_space();
19945            for (i, expr) in exprs.iter().enumerate() {
19946                if i > 0 {
19947                    self.write(", ");
19948                }
19949                self.generate_expression(expr)?;
19950            }
19951        }
19952        Ok(())
19953    }
19954
19955    /// Writes ORDER BY / SORT BY clause with Ordered expressions
19956    fn write_order_clause(&mut self, keyword: &str, orderings: &[Ordered]) -> Result<()> {
19957        if orderings.is_empty() {
19958            return Ok(());
19959        }
19960
19961        if self.config.pretty {
19962            self.write_newline();
19963            self.write_indent();
19964            self.write_keyword(keyword);
19965            self.write_newline();
19966            self.indent_level += 1;
19967            for (i, ordered) in orderings.iter().enumerate() {
19968                if i > 0 {
19969                    self.write(",");
19970                    self.write_newline();
19971                }
19972                self.write_indent();
19973                self.generate_ordered(ordered)?;
19974            }
19975            self.indent_level -= 1;
19976        } else {
19977            self.write_space();
19978            self.write_keyword(keyword);
19979            self.write_space();
19980            for (i, ordered) in orderings.iter().enumerate() {
19981                if i > 0 {
19982                    self.write(", ");
19983                }
19984                self.generate_ordered(ordered)?;
19985            }
19986        }
19987        Ok(())
19988    }
19989
19990    /// Writes WINDOW clause with named window definitions
19991    fn write_window_clause(&mut self, windows: &[NamedWindow]) -> Result<()> {
19992        if windows.is_empty() {
19993            return Ok(());
19994        }
19995
19996        if self.config.pretty {
19997            self.write_newline();
19998            self.write_indent();
19999            self.write_keyword("WINDOW");
20000            self.write_newline();
20001            self.indent_level += 1;
20002            for (i, named_window) in windows.iter().enumerate() {
20003                if i > 0 {
20004                    self.write(",");
20005                    self.write_newline();
20006                }
20007                self.write_indent();
20008                self.generate_identifier(&named_window.name)?;
20009                self.write_space();
20010                self.write_keyword("AS");
20011                self.write(" (");
20012                self.generate_over(&named_window.spec)?;
20013                self.write(")");
20014            }
20015            self.indent_level -= 1;
20016        } else {
20017            self.write_space();
20018            self.write_keyword("WINDOW");
20019            self.write_space();
20020            for (i, named_window) in windows.iter().enumerate() {
20021                if i > 0 {
20022                    self.write(", ");
20023                }
20024                self.generate_identifier(&named_window.name)?;
20025                self.write_space();
20026                self.write_keyword("AS");
20027                self.write(" (");
20028                self.generate_over(&named_window.spec)?;
20029                self.write(")");
20030            }
20031        }
20032        Ok(())
20033    }
20034
20035    // === BATCH-GENERATED STUB METHODS (481 variants) ===
20036    fn generate_ai_agg(&mut self, e: &AIAgg) -> Result<()> {
20037        // AI_AGG(this, expression)
20038        self.write_keyword("AI_AGG");
20039        self.write("(");
20040        self.generate_expression(&e.this)?;
20041        self.write(", ");
20042        self.generate_expression(&e.expression)?;
20043        self.write(")");
20044        Ok(())
20045    }
20046
20047    fn generate_ai_classify(&mut self, e: &AIClassify) -> Result<()> {
20048        // AI_CLASSIFY(input, [categories], [config])
20049        self.write_keyword("AI_CLASSIFY");
20050        self.write("(");
20051        self.generate_expression(&e.this)?;
20052        if let Some(categories) = &e.categories {
20053            self.write(", ");
20054            self.generate_expression(categories)?;
20055        }
20056        if let Some(config) = &e.config {
20057            self.write(", ");
20058            self.generate_expression(config)?;
20059        }
20060        self.write(")");
20061        Ok(())
20062    }
20063
20064    fn generate_add_partition(&mut self, e: &AddPartition) -> Result<()> {
20065        // Python: return f"ADD {exists}{self.sql(expression.this)}{location}"
20066        self.write_keyword("ADD");
20067        self.write_space();
20068        if e.exists {
20069            self.write_keyword("IF NOT EXISTS");
20070            self.write_space();
20071        }
20072        self.generate_expression(&e.this)?;
20073        if let Some(location) = &e.location {
20074            self.write_space();
20075            self.generate_expression(location)?;
20076        }
20077        Ok(())
20078    }
20079
20080    fn generate_algorithm_property(&mut self, e: &AlgorithmProperty) -> Result<()> {
20081        // Python: return f"ALGORITHM={self.sql(expression, 'this')}"
20082        self.write_keyword("ALGORITHM");
20083        self.write("=");
20084        self.generate_expression(&e.this)?;
20085        Ok(())
20086    }
20087
20088    fn generate_aliases(&mut self, e: &Aliases) -> Result<()> {
20089        // Python: return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
20090        self.generate_expression(&e.this)?;
20091        self.write_space();
20092        self.write_keyword("AS");
20093        self.write(" (");
20094        for (i, expr) in e.expressions.iter().enumerate() {
20095            if i > 0 {
20096                self.write(", ");
20097            }
20098            self.generate_expression(expr)?;
20099        }
20100        self.write(")");
20101        Ok(())
20102    }
20103
20104    fn generate_allowed_values_property(&mut self, e: &AllowedValuesProperty) -> Result<()> {
20105        // Python: return f"ALLOWED_VALUES {self.expressions(e, flat=True)}"
20106        self.write_keyword("ALLOWED_VALUES");
20107        self.write_space();
20108        for (i, expr) in e.expressions.iter().enumerate() {
20109            if i > 0 {
20110                self.write(", ");
20111            }
20112            self.generate_expression(expr)?;
20113        }
20114        Ok(())
20115    }
20116
20117    fn generate_alter_column(&mut self, e: &AlterColumn) -> Result<()> {
20118        // Python: complex logic based on dtype, default, comment, visible, etc.
20119        self.write_keyword("ALTER COLUMN");
20120        self.write_space();
20121        self.generate_expression(&e.this)?;
20122
20123        if let Some(dtype) = &e.dtype {
20124            self.write_space();
20125            self.write_keyword("SET DATA TYPE");
20126            self.write_space();
20127            self.generate_expression(dtype)?;
20128            if let Some(collate) = &e.collate {
20129                self.write_space();
20130                self.write_keyword("COLLATE");
20131                self.write_space();
20132                self.generate_expression(collate)?;
20133            }
20134            if let Some(using) = &e.using {
20135                self.write_space();
20136                self.write_keyword("USING");
20137                self.write_space();
20138                self.generate_expression(using)?;
20139            }
20140        } else if let Some(default) = &e.default {
20141            self.write_space();
20142            self.write_keyword("SET DEFAULT");
20143            self.write_space();
20144            self.generate_expression(default)?;
20145        } else if let Some(comment) = &e.comment {
20146            self.write_space();
20147            self.write_keyword("COMMENT");
20148            self.write_space();
20149            self.generate_expression(comment)?;
20150        } else if let Some(drop) = &e.drop {
20151            self.write_space();
20152            self.write_keyword("DROP");
20153            self.write_space();
20154            self.generate_expression(drop)?;
20155        } else if let Some(visible) = &e.visible {
20156            self.write_space();
20157            self.generate_expression(visible)?;
20158        } else if let Some(rename_to) = &e.rename_to {
20159            self.write_space();
20160            self.write_keyword("RENAME TO");
20161            self.write_space();
20162            self.generate_expression(rename_to)?;
20163        } else if let Some(allow_null) = &e.allow_null {
20164            self.write_space();
20165            self.generate_expression(allow_null)?;
20166        }
20167        Ok(())
20168    }
20169
20170    fn generate_alter_session(&mut self, e: &AlterSession) -> Result<()> {
20171        // Python: keyword = "UNSET" if expression.args.get("unset") else "SET"; return f"{keyword} {items_sql}"
20172        self.write_keyword("ALTER SESSION");
20173        self.write_space();
20174        if e.unset.is_some() {
20175            self.write_keyword("UNSET");
20176        } else {
20177            self.write_keyword("SET");
20178        }
20179        self.write_space();
20180        for (i, expr) in e.expressions.iter().enumerate() {
20181            if i > 0 {
20182                self.write(", ");
20183            }
20184            self.generate_expression(expr)?;
20185        }
20186        Ok(())
20187    }
20188
20189    fn generate_alter_set(&mut self, e: &AlterSet) -> Result<()> {
20190        // Python (Snowflake): return f"SET{exprs}{file_format}{copy_options}{tag}"
20191        self.write_keyword("SET");
20192
20193        // Generate option (e.g., AUTHORIZATION, LOGGED, UNLOGGED, etc.)
20194        if let Some(opt) = &e.option {
20195            self.write_space();
20196            self.generate_expression(opt)?;
20197        }
20198
20199        // Generate PROPERTIES (for Trino SET PROPERTIES x = y, ...)
20200        // Check if expressions look like property assignments
20201        if !e.expressions.is_empty() {
20202            // Check if this looks like property assignments (for SET PROPERTIES)
20203            let is_properties = e.expressions.iter().any(|expr| matches!(expr, Expression::Eq(_)));
20204            if is_properties && e.option.is_none() {
20205                self.write_space();
20206                self.write_keyword("PROPERTIES");
20207            }
20208            self.write_space();
20209            for (i, expr) in e.expressions.iter().enumerate() {
20210                if i > 0 {
20211                    self.write(", ");
20212                }
20213                self.generate_expression(expr)?;
20214            }
20215        }
20216
20217        // Generate STAGE_FILE_FORMAT = (...) with space-separated properties
20218        if let Some(file_format) = &e.file_format {
20219            self.write(" ");
20220            self.write_keyword("STAGE_FILE_FORMAT");
20221            self.write(" = (");
20222            self.generate_space_separated_properties(file_format)?;
20223            self.write(")");
20224        }
20225
20226        // Generate STAGE_COPY_OPTIONS = (...) with space-separated properties
20227        if let Some(copy_options) = &e.copy_options {
20228            self.write(" ");
20229            self.write_keyword("STAGE_COPY_OPTIONS");
20230            self.write(" = (");
20231            self.generate_space_separated_properties(copy_options)?;
20232            self.write(")");
20233        }
20234
20235        // Generate TAG ...
20236        if let Some(tag) = &e.tag {
20237            self.write(" ");
20238            self.write_keyword("TAG");
20239            self.write(" ");
20240            self.generate_expression(tag)?;
20241        }
20242
20243        Ok(())
20244    }
20245
20246    /// Generate space-separated properties (for Snowflake STAGE_FILE_FORMAT, etc.)
20247    fn generate_space_separated_properties(&mut self, expr: &Expression) -> Result<()> {
20248        match expr {
20249            Expression::Tuple(t) => {
20250                for (i, prop) in t.expressions.iter().enumerate() {
20251                    if i > 0 {
20252                        self.write(" ");
20253                    }
20254                    self.generate_expression(prop)?;
20255                }
20256            }
20257            _ => {
20258                self.generate_expression(expr)?;
20259            }
20260        }
20261        Ok(())
20262    }
20263
20264    fn generate_alter_sort_key(&mut self, e: &AlterSortKey) -> Result<()> {
20265        // Python: return f"ALTER{compound} SORTKEY {this or expressions}"
20266        self.write_keyword("ALTER");
20267        if e.compound.is_some() {
20268            self.write_space();
20269            self.write_keyword("COMPOUND");
20270        }
20271        self.write_space();
20272        self.write_keyword("SORTKEY");
20273        self.write_space();
20274        if let Some(this) = &e.this {
20275            self.generate_expression(this)?;
20276        } else if !e.expressions.is_empty() {
20277            self.write("(");
20278            for (i, expr) in e.expressions.iter().enumerate() {
20279                if i > 0 {
20280                    self.write(", ");
20281                }
20282                self.generate_expression(expr)?;
20283            }
20284            self.write(")");
20285        }
20286        Ok(())
20287    }
20288
20289    fn generate_analyze(&mut self, e: &Analyze) -> Result<()> {
20290        // Python: return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
20291        self.write_keyword("ANALYZE");
20292        if !e.options.is_empty() {
20293            self.write_space();
20294            for (i, opt) in e.options.iter().enumerate() {
20295                if i > 0 {
20296                    self.write_space();
20297                }
20298                // Write options as keywords (not identifiers) to avoid quoting reserved words like FULL
20299                if let Expression::Identifier(id) = opt {
20300                    self.write_keyword(&id.name);
20301                } else {
20302                    self.generate_expression(opt)?;
20303                }
20304            }
20305        }
20306        if let Some(kind) = &e.kind {
20307            self.write_space();
20308            self.write_keyword(kind);
20309        }
20310        if let Some(this) = &e.this {
20311            self.write_space();
20312            self.generate_expression(this)?;
20313        }
20314        // Column list: ANALYZE tbl(col1, col2) (PostgreSQL)
20315        if !e.columns.is_empty() {
20316            self.write("(");
20317            for (i, col) in e.columns.iter().enumerate() {
20318                if i > 0 {
20319                    self.write(", ");
20320                }
20321                self.write(col);
20322            }
20323            self.write(")");
20324        }
20325        if let Some(partition) = &e.partition {
20326            self.write_space();
20327            self.generate_expression(partition)?;
20328        }
20329        if let Some(mode) = &e.mode {
20330            self.write_space();
20331            self.generate_expression(mode)?;
20332        }
20333        if let Some(expression) = &e.expression {
20334            self.write_space();
20335            self.generate_expression(expression)?;
20336        }
20337        if !e.properties.is_empty() {
20338            self.write_space();
20339            self.write_keyword(self.config.with_properties_prefix);
20340            self.write(" (");
20341            for (i, prop) in e.properties.iter().enumerate() {
20342                if i > 0 {
20343                    self.write(", ");
20344                }
20345                self.generate_expression(prop)?;
20346            }
20347            self.write(")");
20348        }
20349        Ok(())
20350    }
20351
20352    fn generate_analyze_delete(&mut self, e: &AnalyzeDelete) -> Result<()> {
20353        // Python: return f"DELETE{kind} STATISTICS"
20354        self.write_keyword("DELETE");
20355        if let Some(kind) = &e.kind {
20356            self.write_space();
20357            self.write_keyword(kind);
20358        }
20359        self.write_space();
20360        self.write_keyword("STATISTICS");
20361        Ok(())
20362    }
20363
20364    fn generate_analyze_histogram(&mut self, e: &AnalyzeHistogram) -> Result<()> {
20365        // Python: return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
20366        // Write `this` (UPDATE or DROP) as keyword to avoid quoting reserved words
20367        if let Expression::Identifier(id) = e.this.as_ref() {
20368            self.write_keyword(&id.name);
20369        } else {
20370            self.generate_expression(&e.this)?;
20371        }
20372        self.write_space();
20373        self.write_keyword("HISTOGRAM ON");
20374        self.write_space();
20375        for (i, expr) in e.expressions.iter().enumerate() {
20376            if i > 0 {
20377                self.write(", ");
20378            }
20379            self.generate_expression(expr)?;
20380        }
20381        if let Some(expression) = &e.expression {
20382            self.write_space();
20383            self.generate_expression(expression)?;
20384        }
20385        if let Some(update_options) = &e.update_options {
20386            self.write_space();
20387            self.generate_expression(update_options)?;
20388            self.write_space();
20389            self.write_keyword("UPDATE");
20390        }
20391        Ok(())
20392    }
20393
20394    fn generate_analyze_list_chained_rows(&mut self, e: &AnalyzeListChainedRows) -> Result<()> {
20395        // Python: return f"LIST CHAINED ROWS{inner_expression}"
20396        self.write_keyword("LIST CHAINED ROWS");
20397        if let Some(expression) = &e.expression {
20398            self.write_space();
20399            self.write_keyword("INTO");
20400            self.write_space();
20401            self.generate_expression(expression)?;
20402        }
20403        Ok(())
20404    }
20405
20406    fn generate_analyze_sample(&mut self, e: &AnalyzeSample) -> Result<()> {
20407        // Python: return f"SAMPLE {sample} {kind}"
20408        self.write_keyword("SAMPLE");
20409        self.write_space();
20410        if let Some(sample) = &e.sample {
20411            self.generate_expression(sample)?;
20412            self.write_space();
20413        }
20414        self.write_keyword(&e.kind);
20415        Ok(())
20416    }
20417
20418    fn generate_analyze_statistics(&mut self, e: &AnalyzeStatistics) -> Result<()> {
20419        // Python: return f"{kind}{option} STATISTICS{this}{columns}"
20420        self.write_keyword(&e.kind);
20421        if let Some(option) = &e.option {
20422            self.write_space();
20423            self.generate_expression(option)?;
20424        }
20425        self.write_space();
20426        self.write_keyword("STATISTICS");
20427        if let Some(this) = &e.this {
20428            self.write_space();
20429            self.generate_expression(this)?;
20430        }
20431        if !e.expressions.is_empty() {
20432            self.write_space();
20433            for (i, expr) in e.expressions.iter().enumerate() {
20434                if i > 0 {
20435                    self.write(", ");
20436                }
20437                self.generate_expression(expr)?;
20438            }
20439        }
20440        Ok(())
20441    }
20442
20443    fn generate_analyze_validate(&mut self, e: &AnalyzeValidate) -> Result<()> {
20444        // Python: return f"VALIDATE {kind}{this}{inner_expression}"
20445        self.write_keyword("VALIDATE");
20446        self.write_space();
20447        self.write_keyword(&e.kind);
20448        if let Some(this) = &e.this {
20449            self.write_space();
20450            // this is a keyword string like "UPDATE", "CASCADE FAST", etc. - write as keywords
20451            if let Expression::Identifier(id) = this.as_ref() {
20452                self.write_keyword(&id.name);
20453            } else {
20454                self.generate_expression(this)?;
20455            }
20456        }
20457        if let Some(expression) = &e.expression {
20458            self.write_space();
20459            self.write_keyword("INTO");
20460            self.write_space();
20461            self.generate_expression(expression)?;
20462        }
20463        Ok(())
20464    }
20465
20466    fn generate_analyze_with(&mut self, e: &AnalyzeWith) -> Result<()> {
20467        // Python: return f"WITH {expressions}"
20468        self.write_keyword("WITH");
20469        self.write_space();
20470        for (i, expr) in e.expressions.iter().enumerate() {
20471            if i > 0 {
20472                self.write(", ");
20473            }
20474            self.generate_expression(expr)?;
20475        }
20476        Ok(())
20477    }
20478
20479    fn generate_anonymous(&mut self, e: &Anonymous) -> Result<()> {
20480        // Anonymous represents a generic function call: FUNC_NAME(args...)
20481        // Python: return self.func(self.sql(expression, "this"), *expression.expressions)
20482        self.generate_expression(&e.this)?;
20483        self.write("(");
20484        for (i, arg) in e.expressions.iter().enumerate() {
20485            if i > 0 {
20486                self.write(", ");
20487            }
20488            self.generate_expression(arg)?;
20489        }
20490        self.write(")");
20491        Ok(())
20492    }
20493
20494    fn generate_anonymous_agg_func(&mut self, e: &AnonymousAggFunc) -> Result<()> {
20495        // Same as Anonymous but for aggregate functions
20496        self.generate_expression(&e.this)?;
20497        self.write("(");
20498        for (i, arg) in e.expressions.iter().enumerate() {
20499            if i > 0 {
20500                self.write(", ");
20501            }
20502            self.generate_expression(arg)?;
20503        }
20504        self.write(")");
20505        Ok(())
20506    }
20507
20508    fn generate_apply(&mut self, e: &Apply) -> Result<()> {
20509        // Python: return f"{this} APPLY({expr})"
20510        self.generate_expression(&e.this)?;
20511        self.write_space();
20512        self.write_keyword("APPLY");
20513        self.write("(");
20514        self.generate_expression(&e.expression)?;
20515        self.write(")");
20516        Ok(())
20517    }
20518
20519    fn generate_approx_percentile_estimate(&mut self, e: &ApproxPercentileEstimate) -> Result<()> {
20520        // APPROX_PERCENTILE_ESTIMATE(this, percentile)
20521        self.write_keyword("APPROX_PERCENTILE_ESTIMATE");
20522        self.write("(");
20523        self.generate_expression(&e.this)?;
20524        if let Some(percentile) = &e.percentile {
20525            self.write(", ");
20526            self.generate_expression(percentile)?;
20527        }
20528        self.write(")");
20529        Ok(())
20530    }
20531
20532    fn generate_approx_quantile(&mut self, e: &ApproxQuantile) -> Result<()> {
20533        // APPROX_QUANTILE(this, quantile[, accuracy][, weight])
20534        self.write_keyword("APPROX_QUANTILE");
20535        self.write("(");
20536        self.generate_expression(&e.this)?;
20537        if let Some(quantile) = &e.quantile {
20538            self.write(", ");
20539            self.generate_expression(quantile)?;
20540        }
20541        if let Some(accuracy) = &e.accuracy {
20542            self.write(", ");
20543            self.generate_expression(accuracy)?;
20544        }
20545        if let Some(weight) = &e.weight {
20546            self.write(", ");
20547            self.generate_expression(weight)?;
20548        }
20549        self.write(")");
20550        Ok(())
20551    }
20552
20553    fn generate_approx_quantiles(&mut self, e: &ApproxQuantiles) -> Result<()> {
20554        // APPROX_QUANTILES(this, expression)
20555        self.write_keyword("APPROX_QUANTILES");
20556        self.write("(");
20557        self.generate_expression(&e.this)?;
20558        if let Some(expression) = &e.expression {
20559            self.write(", ");
20560            self.generate_expression(expression)?;
20561        }
20562        self.write(")");
20563        Ok(())
20564    }
20565
20566    fn generate_approx_top_k(&mut self, e: &ApproxTopK) -> Result<()> {
20567        // APPROX_TOP_K(this[, expression][, counters])
20568        self.write_keyword("APPROX_TOP_K");
20569        self.write("(");
20570        self.generate_expression(&e.this)?;
20571        if let Some(expression) = &e.expression {
20572            self.write(", ");
20573            self.generate_expression(expression)?;
20574        }
20575        if let Some(counters) = &e.counters {
20576            self.write(", ");
20577            self.generate_expression(counters)?;
20578        }
20579        self.write(")");
20580        Ok(())
20581    }
20582
20583    fn generate_approx_top_k_accumulate(&mut self, e: &ApproxTopKAccumulate) -> Result<()> {
20584        // APPROX_TOP_K_ACCUMULATE(this[, expression])
20585        self.write_keyword("APPROX_TOP_K_ACCUMULATE");
20586        self.write("(");
20587        self.generate_expression(&e.this)?;
20588        if let Some(expression) = &e.expression {
20589            self.write(", ");
20590            self.generate_expression(expression)?;
20591        }
20592        self.write(")");
20593        Ok(())
20594    }
20595
20596    fn generate_approx_top_k_combine(&mut self, e: &ApproxTopKCombine) -> Result<()> {
20597        // APPROX_TOP_K_COMBINE(this[, expression])
20598        self.write_keyword("APPROX_TOP_K_COMBINE");
20599        self.write("(");
20600        self.generate_expression(&e.this)?;
20601        if let Some(expression) = &e.expression {
20602            self.write(", ");
20603            self.generate_expression(expression)?;
20604        }
20605        self.write(")");
20606        Ok(())
20607    }
20608
20609    fn generate_approx_top_k_estimate(&mut self, e: &ApproxTopKEstimate) -> Result<()> {
20610        // APPROX_TOP_K_ESTIMATE(this[, expression])
20611        self.write_keyword("APPROX_TOP_K_ESTIMATE");
20612        self.write("(");
20613        self.generate_expression(&e.this)?;
20614        if let Some(expression) = &e.expression {
20615            self.write(", ");
20616            self.generate_expression(expression)?;
20617        }
20618        self.write(")");
20619        Ok(())
20620    }
20621
20622    fn generate_approx_top_sum(&mut self, e: &ApproxTopSum) -> Result<()> {
20623        // APPROX_TOP_SUM(this, expression[, count])
20624        self.write_keyword("APPROX_TOP_SUM");
20625        self.write("(");
20626        self.generate_expression(&e.this)?;
20627        self.write(", ");
20628        self.generate_expression(&e.expression)?;
20629        if let Some(count) = &e.count {
20630            self.write(", ");
20631            self.generate_expression(count)?;
20632        }
20633        self.write(")");
20634        Ok(())
20635    }
20636
20637    fn generate_arg_max(&mut self, e: &ArgMax) -> Result<()> {
20638        // ARG_MAX(this, expression[, count])
20639        self.write_keyword("ARG_MAX");
20640        self.write("(");
20641        self.generate_expression(&e.this)?;
20642        self.write(", ");
20643        self.generate_expression(&e.expression)?;
20644        if let Some(count) = &e.count {
20645            self.write(", ");
20646            self.generate_expression(count)?;
20647        }
20648        self.write(")");
20649        Ok(())
20650    }
20651
20652    fn generate_arg_min(&mut self, e: &ArgMin) -> Result<()> {
20653        // ARG_MIN(this, expression[, count])
20654        self.write_keyword("ARG_MIN");
20655        self.write("(");
20656        self.generate_expression(&e.this)?;
20657        self.write(", ");
20658        self.generate_expression(&e.expression)?;
20659        if let Some(count) = &e.count {
20660            self.write(", ");
20661            self.generate_expression(count)?;
20662        }
20663        self.write(")");
20664        Ok(())
20665    }
20666
20667    fn generate_array_all(&mut self, e: &ArrayAll) -> Result<()> {
20668        // ARRAY_ALL(this, expression)
20669        self.write_keyword("ARRAY_ALL");
20670        self.write("(");
20671        self.generate_expression(&e.this)?;
20672        self.write(", ");
20673        self.generate_expression(&e.expression)?;
20674        self.write(")");
20675        Ok(())
20676    }
20677
20678    fn generate_array_any(&mut self, e: &ArrayAny) -> Result<()> {
20679        // ARRAY_ANY(this, expression) - fallback implementation
20680        self.write_keyword("ARRAY_ANY");
20681        self.write("(");
20682        self.generate_expression(&e.this)?;
20683        self.write(", ");
20684        self.generate_expression(&e.expression)?;
20685        self.write(")");
20686        Ok(())
20687    }
20688
20689    fn generate_array_construct_compact(&mut self, e: &ArrayConstructCompact) -> Result<()> {
20690        // ARRAY_CONSTRUCT_COMPACT(expressions...)
20691        self.write_keyword("ARRAY_CONSTRUCT_COMPACT");
20692        self.write("(");
20693        for (i, expr) in e.expressions.iter().enumerate() {
20694            if i > 0 {
20695                self.write(", ");
20696            }
20697            self.generate_expression(expr)?;
20698        }
20699        self.write(")");
20700        Ok(())
20701    }
20702
20703    fn generate_array_sum(&mut self, e: &ArraySum) -> Result<()> {
20704        // ARRAY_SUM(this[, expression])
20705        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
20706            self.write("arraySum");
20707        } else {
20708            self.write_keyword("ARRAY_SUM");
20709        }
20710        self.write("(");
20711        self.generate_expression(&e.this)?;
20712        if let Some(expression) = &e.expression {
20713            self.write(", ");
20714            self.generate_expression(expression)?;
20715        }
20716        self.write(")");
20717        Ok(())
20718    }
20719
20720    fn generate_at_index(&mut self, e: &AtIndex) -> Result<()> {
20721        // Python: return f"{this} AT {index}"
20722        self.generate_expression(&e.this)?;
20723        self.write_space();
20724        self.write_keyword("AT");
20725        self.write_space();
20726        self.generate_expression(&e.expression)?;
20727        Ok(())
20728    }
20729
20730    fn generate_attach(&mut self, e: &Attach) -> Result<()> {
20731        // Python: return f"ATTACH{exists_sql} {this}{expressions}"
20732        self.write_keyword("ATTACH");
20733        if e.exists {
20734            self.write_space();
20735            self.write_keyword("IF NOT EXISTS");
20736        }
20737        self.write_space();
20738        self.generate_expression(&e.this)?;
20739        if !e.expressions.is_empty() {
20740            self.write(" (");
20741            for (i, expr) in e.expressions.iter().enumerate() {
20742                if i > 0 {
20743                    self.write(", ");
20744                }
20745                self.generate_expression(expr)?;
20746            }
20747            self.write(")");
20748        }
20749        Ok(())
20750    }
20751
20752    fn generate_attach_option(&mut self, e: &AttachOption) -> Result<()> {
20753        // AttachOption: this [expression]
20754        // Python sqlglot: no equals sign, just space-separated
20755        self.generate_expression(&e.this)?;
20756        if let Some(expression) = &e.expression {
20757            self.write_space();
20758            self.generate_expression(expression)?;
20759        }
20760        Ok(())
20761    }
20762
20763    /// Generate the auto_increment keyword and options for a column definition.
20764    /// Different dialects use different syntax: IDENTITY, AUTOINCREMENT, AUTO_INCREMENT,
20765    /// GENERATED AS IDENTITY, etc.
20766    fn generate_auto_increment_keyword(&mut self, col: &crate::expressions::ColumnDef) -> Result<()> {
20767        use crate::dialects::DialectType;
20768        if matches!(self.config.dialect, Some(DialectType::Redshift)) {
20769            self.write_keyword("IDENTITY");
20770            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20771                self.write("(");
20772                if let Some(ref start) = col.auto_increment_start {
20773                    self.generate_expression(start)?;
20774                } else {
20775                    self.write("0");
20776                }
20777                self.write(", ");
20778                if let Some(ref inc) = col.auto_increment_increment {
20779                    self.generate_expression(inc)?;
20780                } else {
20781                    self.write("1");
20782                }
20783                self.write(")");
20784            }
20785        } else if matches!(self.config.dialect, Some(DialectType::Snowflake) | Some(DialectType::SQLite)) {
20786            self.write_keyword("AUTOINCREMENT");
20787            if let Some(ref start) = col.auto_increment_start {
20788                self.write_space();
20789                self.write_keyword("START");
20790                self.write_space();
20791                self.generate_expression(start)?;
20792            }
20793            if let Some(ref inc) = col.auto_increment_increment {
20794                self.write_space();
20795                self.write_keyword("INCREMENT");
20796                self.write_space();
20797                self.generate_expression(inc)?;
20798            }
20799            if let Some(order) = col.auto_increment_order {
20800                self.write_space();
20801                if order {
20802                    self.write_keyword("ORDER");
20803                } else {
20804                    self.write_keyword("NOORDER");
20805                }
20806            }
20807        } else if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) {
20808            self.write_keyword("GENERATED BY DEFAULT AS IDENTITY");
20809            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20810                self.write(" (");
20811                let mut first = true;
20812                if let Some(ref start) = col.auto_increment_start {
20813                    self.write_keyword("START WITH");
20814                    self.write_space();
20815                    self.generate_expression(start)?;
20816                    first = false;
20817                }
20818                if let Some(ref inc) = col.auto_increment_increment {
20819                    if !first { self.write_space(); }
20820                    self.write_keyword("INCREMENT BY");
20821                    self.write_space();
20822                    self.generate_expression(inc)?;
20823                }
20824                self.write(")");
20825            }
20826        } else if matches!(self.config.dialect, Some(DialectType::Databricks)) {
20827            self.write_keyword("GENERATED ALWAYS AS IDENTITY");
20828            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20829                self.write(" (");
20830                let mut first = true;
20831                if let Some(ref start) = col.auto_increment_start {
20832                    self.write_keyword("START WITH");
20833                    self.write_space();
20834                    self.generate_expression(start)?;
20835                    first = false;
20836                }
20837                if let Some(ref inc) = col.auto_increment_increment {
20838                    if !first { self.write_space(); }
20839                    self.write_keyword("INCREMENT BY");
20840                    self.write_space();
20841                    self.generate_expression(inc)?;
20842                }
20843                self.write(")");
20844            }
20845        } else if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
20846            self.write_keyword("IDENTITY");
20847            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20848                self.write("(");
20849                if let Some(ref start) = col.auto_increment_start {
20850                    self.generate_expression(start)?;
20851                } else {
20852                    self.write("0");
20853                }
20854                self.write(", ");
20855                if let Some(ref inc) = col.auto_increment_increment {
20856                    self.generate_expression(inc)?;
20857                } else {
20858                    self.write("1");
20859                }
20860                self.write(")");
20861            }
20862        } else {
20863            self.write_keyword("AUTO_INCREMENT");
20864            if let Some(ref start) = col.auto_increment_start {
20865                self.write_space();
20866                self.write_keyword("START");
20867                self.write_space();
20868                self.generate_expression(start)?;
20869            }
20870            if let Some(ref inc) = col.auto_increment_increment {
20871                self.write_space();
20872                self.write_keyword("INCREMENT");
20873                self.write_space();
20874                self.generate_expression(inc)?;
20875            }
20876            if let Some(order) = col.auto_increment_order {
20877                self.write_space();
20878                if order {
20879                    self.write_keyword("ORDER");
20880                } else {
20881                    self.write_keyword("NOORDER");
20882                }
20883            }
20884        }
20885        Ok(())
20886    }
20887
20888    fn generate_auto_increment_property(&mut self, e: &AutoIncrementProperty) -> Result<()> {
20889        // AUTO_INCREMENT=value
20890        self.write_keyword("AUTO_INCREMENT");
20891        self.write("=");
20892        self.generate_expression(&e.this)?;
20893        Ok(())
20894    }
20895
20896    fn generate_auto_refresh_property(&mut self, e: &AutoRefreshProperty) -> Result<()> {
20897        // AUTO_REFRESH=value
20898        self.write_keyword("AUTO_REFRESH");
20899        self.write("=");
20900        self.generate_expression(&e.this)?;
20901        Ok(())
20902    }
20903
20904    fn generate_backup_property(&mut self, e: &BackupProperty) -> Result<()> {
20905        // BACKUP YES|NO (Redshift syntax uses space, not equals)
20906        self.write_keyword("BACKUP");
20907        self.write_space();
20908        self.generate_expression(&e.this)?;
20909        Ok(())
20910    }
20911
20912    fn generate_base64_decode_binary(&mut self, e: &Base64DecodeBinary) -> Result<()> {
20913        // BASE64_DECODE_BINARY(this[, alphabet])
20914        self.write_keyword("BASE64_DECODE_BINARY");
20915        self.write("(");
20916        self.generate_expression(&e.this)?;
20917        if let Some(alphabet) = &e.alphabet {
20918            self.write(", ");
20919            self.generate_expression(alphabet)?;
20920        }
20921        self.write(")");
20922        Ok(())
20923    }
20924
20925    fn generate_base64_decode_string(&mut self, e: &Base64DecodeString) -> Result<()> {
20926        // BASE64_DECODE_STRING(this[, alphabet])
20927        self.write_keyword("BASE64_DECODE_STRING");
20928        self.write("(");
20929        self.generate_expression(&e.this)?;
20930        if let Some(alphabet) = &e.alphabet {
20931            self.write(", ");
20932            self.generate_expression(alphabet)?;
20933        }
20934        self.write(")");
20935        Ok(())
20936    }
20937
20938    fn generate_base64_encode(&mut self, e: &Base64Encode) -> Result<()> {
20939        // BASE64_ENCODE(this[, max_line_length][, alphabet])
20940        self.write_keyword("BASE64_ENCODE");
20941        self.write("(");
20942        self.generate_expression(&e.this)?;
20943        if let Some(max_line_length) = &e.max_line_length {
20944            self.write(", ");
20945            self.generate_expression(max_line_length)?;
20946        }
20947        if let Some(alphabet) = &e.alphabet {
20948            self.write(", ");
20949            self.generate_expression(alphabet)?;
20950        }
20951        self.write(")");
20952        Ok(())
20953    }
20954
20955    fn generate_block_compression_property(&mut self, e: &BlockCompressionProperty) -> Result<()> {
20956        // BLOCKCOMPRESSION=... (complex Teradata property)
20957        self.write_keyword("BLOCKCOMPRESSION");
20958        self.write("=");
20959        if let Some(autotemp) = &e.autotemp {
20960            self.write_keyword("AUTOTEMP");
20961            self.write("(");
20962            self.generate_expression(autotemp)?;
20963            self.write(")");
20964        }
20965        if let Some(always) = &e.always {
20966            self.generate_expression(always)?;
20967        }
20968        if let Some(default) = &e.default {
20969            self.generate_expression(default)?;
20970        }
20971        if let Some(manual) = &e.manual {
20972            self.generate_expression(manual)?;
20973        }
20974        if let Some(never) = &e.never {
20975            self.generate_expression(never)?;
20976        }
20977        Ok(())
20978    }
20979
20980    fn generate_booland(&mut self, e: &Booland) -> Result<()> {
20981        // Python: return f"(({self.sql(expression, 'this')}) AND ({self.sql(expression, 'expression')}))"
20982        self.write("((");
20983        self.generate_expression(&e.this)?;
20984        self.write(") ");
20985        self.write_keyword("AND");
20986        self.write(" (");
20987        self.generate_expression(&e.expression)?;
20988        self.write("))");
20989        Ok(())
20990    }
20991
20992    fn generate_boolor(&mut self, e: &Boolor) -> Result<()> {
20993        // Python: return f"(({self.sql(expression, 'this')}) OR ({self.sql(expression, 'expression')}))"
20994        self.write("((");
20995        self.generate_expression(&e.this)?;
20996        self.write(") ");
20997        self.write_keyword("OR");
20998        self.write(" (");
20999        self.generate_expression(&e.expression)?;
21000        self.write("))");
21001        Ok(())
21002    }
21003
21004    fn generate_build_property(&mut self, e: &BuildProperty) -> Result<()> {
21005        // BUILD value (e.g., BUILD IMMEDIATE, BUILD DEFERRED)
21006        self.write_keyword("BUILD");
21007        self.write_space();
21008        self.generate_expression(&e.this)?;
21009        Ok(())
21010    }
21011
21012    fn generate_byte_string(&mut self, e: &ByteString) -> Result<()> {
21013        // Byte string literal like B'...' or X'...'
21014        self.generate_expression(&e.this)?;
21015        Ok(())
21016    }
21017
21018    fn generate_case_specific_column_constraint(&mut self, e: &CaseSpecificColumnConstraint) -> Result<()> {
21019        // CASESPECIFIC or NOT CASESPECIFIC (Teradata)
21020        if e.not_.is_some() {
21021            self.write_keyword("NOT");
21022            self.write_space();
21023        }
21024        self.write_keyword("CASESPECIFIC");
21025        Ok(())
21026    }
21027
21028    fn generate_cast_to_str_type(&mut self, e: &CastToStrType) -> Result<()> {
21029        // Cast to string type (dialect-specific)
21030        self.write_keyword("CAST");
21031        self.write("(");
21032        self.generate_expression(&e.this)?;
21033        if self.config.dialect == Some(DialectType::ClickHouse) {
21034            // ClickHouse: CAST(expr, 'type_string')
21035            self.write(", ");
21036        } else {
21037            self.write_space();
21038            self.write_keyword("AS");
21039            self.write_space();
21040        }
21041        if let Some(to) = &e.to {
21042            self.generate_expression(to)?;
21043        }
21044        self.write(")");
21045        Ok(())
21046    }
21047
21048    fn generate_changes(&mut self, e: &Changes) -> Result<()> {
21049        // CHANGES (INFORMATION => value) AT|BEFORE (...) END (...)
21050        // Python: f"CHANGES ({information}){at_before}{end}"
21051        self.write_keyword("CHANGES");
21052        self.write(" (");
21053        if let Some(information) = &e.information {
21054            self.write_keyword("INFORMATION");
21055            self.write(" => ");
21056            self.generate_expression(information)?;
21057        }
21058        self.write(")");
21059        // at_before and end are HistoricalData expressions that generate their own keywords
21060        if let Some(at_before) = &e.at_before {
21061            self.write(" ");
21062            self.generate_expression(at_before)?;
21063        }
21064        if let Some(end) = &e.end {
21065            self.write(" ");
21066            self.generate_expression(end)?;
21067        }
21068        Ok(())
21069    }
21070
21071    fn generate_character_set_column_constraint(&mut self, e: &CharacterSetColumnConstraint) -> Result<()> {
21072        // CHARACTER SET charset_name
21073        self.write_keyword("CHARACTER SET");
21074        self.write_space();
21075        self.generate_expression(&e.this)?;
21076        Ok(())
21077    }
21078
21079    fn generate_character_set_property(&mut self, e: &CharacterSetProperty) -> Result<()> {
21080        // [DEFAULT] CHARACTER SET=value
21081        if e.default.is_some() {
21082            self.write_keyword("DEFAULT");
21083            self.write_space();
21084        }
21085        self.write_keyword("CHARACTER SET");
21086        self.write("=");
21087        self.generate_expression(&e.this)?;
21088        Ok(())
21089    }
21090
21091    fn generate_check_column_constraint(&mut self, e: &CheckColumnConstraint) -> Result<()> {
21092        // Python: return f"CHECK ({self.sql(expression, 'this')}){enforced}"
21093        self.write_keyword("CHECK");
21094        self.write(" (");
21095        self.generate_expression(&e.this)?;
21096        self.write(")");
21097        if e.enforced.is_some() {
21098            self.write_space();
21099            self.write_keyword("ENFORCED");
21100        }
21101        Ok(())
21102    }
21103
21104    fn generate_check_json(&mut self, e: &CheckJson) -> Result<()> {
21105        // CHECK_JSON(this)
21106        self.write_keyword("CHECK_JSON");
21107        self.write("(");
21108        self.generate_expression(&e.this)?;
21109        self.write(")");
21110        Ok(())
21111    }
21112
21113    fn generate_check_xml(&mut self, e: &CheckXml) -> Result<()> {
21114        // CHECK_XML(this)
21115        self.write_keyword("CHECK_XML");
21116        self.write("(");
21117        self.generate_expression(&e.this)?;
21118        self.write(")");
21119        Ok(())
21120    }
21121
21122    fn generate_checksum_property(&mut self, e: &ChecksumProperty) -> Result<()> {
21123        // CHECKSUM=[ON|OFF|DEFAULT]
21124        self.write_keyword("CHECKSUM");
21125        self.write("=");
21126        if e.on.is_some() {
21127            self.write_keyword("ON");
21128        } else if e.default.is_some() {
21129            self.write_keyword("DEFAULT");
21130        } else {
21131            self.write_keyword("OFF");
21132        }
21133        Ok(())
21134    }
21135
21136    fn generate_clone(&mut self, e: &Clone) -> Result<()> {
21137        // Python: return f"{shallow}{keyword} {this}"
21138        if e.shallow.is_some() {
21139            self.write_keyword("SHALLOW");
21140            self.write_space();
21141        }
21142        if e.copy.is_some() {
21143            self.write_keyword("COPY");
21144        } else {
21145            self.write_keyword("CLONE");
21146        }
21147        self.write_space();
21148        self.generate_expression(&e.this)?;
21149        Ok(())
21150    }
21151
21152    fn generate_cluster_by(&mut self, e: &ClusterBy) -> Result<()> {
21153        // CLUSTER BY (expressions)
21154        self.write_keyword("CLUSTER BY");
21155        self.write(" (");
21156        for (i, ord) in e.expressions.iter().enumerate() {
21157            if i > 0 {
21158                self.write(", ");
21159            }
21160            self.generate_ordered(ord)?;
21161        }
21162        self.write(")");
21163        Ok(())
21164    }
21165
21166    fn generate_clustered_by_property(&mut self, e: &ClusteredByProperty) -> Result<()> {
21167        // Python: return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
21168        self.write_keyword("CLUSTERED BY");
21169        self.write(" (");
21170        for (i, expr) in e.expressions.iter().enumerate() {
21171            if i > 0 {
21172                self.write(", ");
21173            }
21174            self.generate_expression(expr)?;
21175        }
21176        self.write(")");
21177        if let Some(sorted_by) = &e.sorted_by {
21178            self.write_space();
21179            self.write_keyword("SORTED BY");
21180            self.write(" (");
21181            // Unwrap Tuple to avoid double parentheses
21182            if let Expression::Tuple(t) = sorted_by.as_ref() {
21183                for (i, expr) in t.expressions.iter().enumerate() {
21184                    if i > 0 {
21185                        self.write(", ");
21186                    }
21187                    self.generate_expression(expr)?;
21188                }
21189            } else {
21190                self.generate_expression(sorted_by)?;
21191            }
21192            self.write(")");
21193        }
21194        if let Some(buckets) = &e.buckets {
21195            self.write_space();
21196            self.write_keyword("INTO");
21197            self.write_space();
21198            self.generate_expression(buckets)?;
21199            self.write_space();
21200            self.write_keyword("BUCKETS");
21201        }
21202        Ok(())
21203    }
21204
21205    fn generate_collate_property(&mut self, e: &CollateProperty) -> Result<()> {
21206        // [DEFAULT] COLLATE [=] value
21207        // BigQuery uses space: DEFAULT COLLATE 'en'
21208        // Others use equals: COLLATE='en'
21209        if e.default.is_some() {
21210            self.write_keyword("DEFAULT");
21211            self.write_space();
21212        }
21213        self.write_keyword("COLLATE");
21214        // BigQuery uses space between COLLATE and value
21215        match self.config.dialect {
21216            Some(DialectType::BigQuery) => self.write_space(),
21217            _ => self.write("="),
21218        }
21219        self.generate_expression(&e.this)?;
21220        Ok(())
21221    }
21222
21223    fn generate_column_constraint(&mut self, e: &ColumnConstraint) -> Result<()> {
21224        // ColumnConstraint is an enum
21225        match e {
21226            ColumnConstraint::NotNull => {
21227                self.write_keyword("NOT NULL");
21228            }
21229            ColumnConstraint::Null => {
21230                self.write_keyword("NULL");
21231            }
21232            ColumnConstraint::Unique => {
21233                self.write_keyword("UNIQUE");
21234            }
21235            ColumnConstraint::PrimaryKey => {
21236                self.write_keyword("PRIMARY KEY");
21237            }
21238            ColumnConstraint::Default(expr) => {
21239                self.write_keyword("DEFAULT");
21240                self.write_space();
21241                self.generate_expression(expr)?;
21242            }
21243            ColumnConstraint::Check(expr) => {
21244                self.write_keyword("CHECK");
21245                self.write(" (");
21246                self.generate_expression(expr)?;
21247                self.write(")");
21248            }
21249            ColumnConstraint::References(fk_ref) => {
21250                if fk_ref.has_foreign_key_keywords {
21251                    self.write_keyword("FOREIGN KEY");
21252                    self.write_space();
21253                }
21254                self.write_keyword("REFERENCES");
21255                self.write_space();
21256                self.generate_table(&fk_ref.table)?;
21257                if !fk_ref.columns.is_empty() {
21258                    self.write(" (");
21259                    for (i, col) in fk_ref.columns.iter().enumerate() {
21260                        if i > 0 {
21261                            self.write(", ");
21262                        }
21263                        self.generate_identifier(col)?;
21264                    }
21265                    self.write(")");
21266                }
21267            }
21268            ColumnConstraint::GeneratedAsIdentity(gen) => {
21269                self.write_keyword("GENERATED");
21270                self.write_space();
21271                if gen.always {
21272                    self.write_keyword("ALWAYS");
21273                } else {
21274                    self.write_keyword("BY DEFAULT");
21275                    if gen.on_null {
21276                        self.write_space();
21277                        self.write_keyword("ON NULL");
21278                    }
21279                }
21280                self.write_space();
21281                self.write_keyword("AS IDENTITY");
21282            }
21283            ColumnConstraint::Collate(collation) => {
21284                self.write_keyword("COLLATE");
21285                self.write_space();
21286                self.generate_identifier(collation)?;
21287            }
21288            ColumnConstraint::Comment(comment) => {
21289                self.write_keyword("COMMENT");
21290                self.write(" '");
21291                self.write(comment);
21292                self.write("'");
21293            }
21294            ColumnConstraint::ComputedColumn(cc) => {
21295                self.generate_computed_column_inline(cc)?;
21296            }
21297            ColumnConstraint::GeneratedAsRow(gar) => {
21298                self.generate_generated_as_row_inline(gar)?;
21299            }
21300            ColumnConstraint::Tags(tags) => {
21301                self.write_keyword("TAG");
21302                self.write(" (");
21303                for (i, expr) in tags.expressions.iter().enumerate() {
21304                    if i > 0 {
21305                        self.write(", ");
21306                    }
21307                    self.generate_expression(expr)?;
21308                }
21309                self.write(")");
21310            }
21311            ColumnConstraint::Path(path_expr) => {
21312                self.write_keyword("PATH");
21313                self.write_space();
21314                self.generate_expression(path_expr)?;
21315            }
21316        }
21317        Ok(())
21318    }
21319
21320    fn generate_column_position(&mut self, e: &ColumnPosition) -> Result<()> {
21321        // ColumnPosition is an enum
21322        match e {
21323            ColumnPosition::First => {
21324                self.write_keyword("FIRST");
21325            }
21326            ColumnPosition::After(ident) => {
21327                self.write_keyword("AFTER");
21328                self.write_space();
21329                self.generate_identifier(ident)?;
21330            }
21331        }
21332        Ok(())
21333    }
21334
21335    fn generate_column_prefix(&mut self, e: &ColumnPrefix) -> Result<()> {
21336        // column(prefix)
21337        self.generate_expression(&e.this)?;
21338        self.write("(");
21339        self.generate_expression(&e.expression)?;
21340        self.write(")");
21341        Ok(())
21342    }
21343
21344    fn generate_columns(&mut self, e: &Columns) -> Result<()> {
21345        // If unpack is true, this came from * COLUMNS(pattern)
21346        // DuckDB syntax: * COLUMNS(c ILIKE '%suffix') or COLUMNS(pattern)
21347        if let Some(ref unpack) = e.unpack {
21348            if let Expression::Boolean(b) = unpack.as_ref() {
21349                if b.value {
21350                    self.write("*");
21351                }
21352            }
21353        }
21354        self.write_keyword("COLUMNS");
21355        self.write("(");
21356        self.generate_expression(&e.this)?;
21357        self.write(")");
21358        Ok(())
21359    }
21360
21361    fn generate_combined_agg_func(&mut self, e: &CombinedAggFunc) -> Result<()> {
21362        // Combined aggregate: FUNC(args) combined
21363        self.generate_expression(&e.this)?;
21364        self.write("(");
21365        for (i, expr) in e.expressions.iter().enumerate() {
21366            if i > 0 {
21367                self.write(", ");
21368            }
21369            self.generate_expression(expr)?;
21370        }
21371        self.write(")");
21372        Ok(())
21373    }
21374
21375    fn generate_combined_parameterized_agg(&mut self, e: &CombinedParameterizedAgg) -> Result<()> {
21376        // Combined parameterized aggregate: FUNC(params)(expressions)
21377        self.generate_expression(&e.this)?;
21378        self.write("(");
21379        for (i, param) in e.params.iter().enumerate() {
21380            if i > 0 {
21381                self.write(", ");
21382            }
21383            self.generate_expression(param)?;
21384        }
21385        self.write(")(");
21386        for (i, expr) in e.expressions.iter().enumerate() {
21387            if i > 0 {
21388                self.write(", ");
21389            }
21390            self.generate_expression(expr)?;
21391        }
21392        self.write(")");
21393        Ok(())
21394    }
21395
21396    fn generate_commit(&mut self, e: &Commit) -> Result<()> {
21397        // COMMIT [TRANSACTION [transaction_name]] [WITH (DELAYED_DURABILITY = ON|OFF)] [AND [NO] CHAIN]
21398        self.write_keyword("COMMIT");
21399
21400        // TSQL always uses COMMIT TRANSACTION
21401        if e.this.is_none() && matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21402            self.write_space();
21403            self.write_keyword("TRANSACTION");
21404        }
21405
21406        // Check if this has TRANSACTION keyword or transaction name
21407        if let Some(this) = &e.this {
21408            // Check if it's just the "TRANSACTION" marker or an actual transaction name
21409            let is_transaction_marker = matches!(
21410                this.as_ref(),
21411                Expression::Identifier(id) if id.name == "TRANSACTION"
21412            );
21413
21414            self.write_space();
21415            self.write_keyword("TRANSACTION");
21416
21417            // If it's a real transaction name, output it
21418            if !is_transaction_marker {
21419                self.write_space();
21420                self.generate_expression(this)?;
21421            }
21422        }
21423
21424        // Output WITH (DELAYED_DURABILITY = ON|OFF) for TSQL
21425        if let Some(durability) = &e.durability {
21426            self.write_space();
21427            self.write_keyword("WITH");
21428            self.write(" (");
21429            self.write_keyword("DELAYED_DURABILITY");
21430            self.write(" = ");
21431            if let Expression::Boolean(BooleanLiteral { value: true }) = durability.as_ref() {
21432                self.write_keyword("ON");
21433            } else {
21434                self.write_keyword("OFF");
21435            }
21436            self.write(")");
21437        }
21438
21439        // Output AND [NO] CHAIN
21440        if let Some(chain) = &e.chain {
21441            self.write_space();
21442            if let Expression::Boolean(BooleanLiteral { value: false }) = chain.as_ref() {
21443                self.write_keyword("AND NO CHAIN");
21444            } else {
21445                self.write_keyword("AND CHAIN");
21446            }
21447        }
21448        Ok(())
21449    }
21450
21451    fn generate_comprehension(&mut self, e: &Comprehension) -> Result<()> {
21452        // Python-style comprehension: [expr FOR var[, pos] IN iterator IF condition]
21453        self.write("[");
21454        self.generate_expression(&e.this)?;
21455        self.write_space();
21456        self.write_keyword("FOR");
21457        self.write_space();
21458        self.generate_expression(&e.expression)?;
21459        // Handle optional position variable (for enumerate-like syntax)
21460        if let Some(pos) = &e.position {
21461            self.write(", ");
21462            self.generate_expression(pos)?;
21463        }
21464        if let Some(iterator) = &e.iterator {
21465            self.write_space();
21466            self.write_keyword("IN");
21467            self.write_space();
21468            self.generate_expression(iterator)?;
21469        }
21470        if let Some(condition) = &e.condition {
21471            self.write_space();
21472            self.write_keyword("IF");
21473            self.write_space();
21474            self.generate_expression(condition)?;
21475        }
21476        self.write("]");
21477        Ok(())
21478    }
21479
21480    fn generate_compress(&mut self, e: &Compress) -> Result<()> {
21481        // COMPRESS(this[, method])
21482        self.write_keyword("COMPRESS");
21483        self.write("(");
21484        self.generate_expression(&e.this)?;
21485        if let Some(method) = &e.method {
21486            self.write(", '");
21487            self.write(method);
21488            self.write("'");
21489        }
21490        self.write(")");
21491        Ok(())
21492    }
21493
21494    fn generate_compress_column_constraint(&mut self, e: &CompressColumnConstraint) -> Result<()> {
21495        // Python: return f"COMPRESS {this}"
21496        self.write_keyword("COMPRESS");
21497        if let Some(this) = &e.this {
21498            self.write_space();
21499            self.generate_expression(this)?;
21500        }
21501        Ok(())
21502    }
21503
21504    fn generate_computed_column_constraint(&mut self, e: &ComputedColumnConstraint) -> Result<()> {
21505        // Python: return f"AS {this}{persisted}"
21506        self.write_keyword("AS");
21507        self.write_space();
21508        self.generate_expression(&e.this)?;
21509        if e.not_null.is_some() {
21510            self.write_space();
21511            self.write_keyword("PERSISTED NOT NULL");
21512        } else if e.persisted.is_some() {
21513            self.write_space();
21514            self.write_keyword("PERSISTED");
21515        }
21516        Ok(())
21517    }
21518
21519    /// Generate a ComputedColumn constraint inline within a column definition.
21520    /// Handles MySQL/PostgreSQL: GENERATED ALWAYS AS (expr) STORED|VIRTUAL
21521    /// Handles TSQL: AS (expr) [PERSISTED] [NOT NULL]
21522    fn generate_computed_column_inline(&mut self, cc: &ComputedColumn) -> Result<()> {
21523        let computed_expr = if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21524            match &*cc.expression {
21525                Expression::Year(y)
21526                    if !matches!(&y.this, Expression::Cast(c) if matches!(c.to, DataType::Date)) =>
21527                {
21528                    let wrapped = Expression::Cast(Box::new(Cast {
21529                        this: y.this.clone(),
21530                        to: DataType::Date,
21531                        trailing_comments: Vec::new(),
21532                        double_colon_syntax: false,
21533                        format: None,
21534                        default: None,
21535                    }));
21536                    Expression::Year(Box::new(UnaryFunc::new(wrapped)))
21537                }
21538                Expression::Function(f)
21539                    if f.name.eq_ignore_ascii_case("YEAR")
21540                        && f.args.len() == 1
21541                        && !matches!(&f.args[0], Expression::Cast(c) if matches!(c.to, DataType::Date)) =>
21542                {
21543                    let wrapped = Expression::Cast(Box::new(Cast {
21544                        this: f.args[0].clone(),
21545                        to: DataType::Date,
21546                        trailing_comments: Vec::new(),
21547                        double_colon_syntax: false,
21548                        format: None,
21549                        default: None,
21550                    }));
21551                    Expression::Function(Box::new(Function::new("YEAR".to_string(), vec![wrapped])))
21552                }
21553                _ => *cc.expression.clone(),
21554            }
21555        } else {
21556            *cc.expression.clone()
21557        };
21558
21559        match cc.persistence_kind.as_deref() {
21560            Some("STORED") | Some("VIRTUAL") => {
21561                // MySQL/PostgreSQL: GENERATED ALWAYS AS (expr) STORED|VIRTUAL
21562                self.write_keyword("GENERATED ALWAYS AS");
21563                self.write(" (");
21564                self.generate_expression(&computed_expr)?;
21565                self.write(")");
21566                self.write_space();
21567                if cc.persisted {
21568                    self.write_keyword("STORED");
21569                } else {
21570                    self.write_keyword("VIRTUAL");
21571                }
21572            }
21573            Some("PERSISTED") => {
21574                // TSQL/SingleStore: AS (expr) PERSISTED [TYPE] [NOT NULL]
21575                self.write_keyword("AS");
21576                self.write(" (");
21577                self.generate_expression(&computed_expr)?;
21578                self.write(")");
21579                self.write_space();
21580                self.write_keyword("PERSISTED");
21581                // Output data type if present (SingleStore: PERSISTED TYPE NOT NULL)
21582                if let Some(ref dt) = cc.data_type {
21583                    self.write_space();
21584                    self.generate_data_type(dt)?;
21585                }
21586                if cc.not_null {
21587                    self.write_space();
21588                    self.write_keyword("NOT NULL");
21589                }
21590            }
21591            _ => {
21592                // Spark/Databricks/Hive: GENERATED ALWAYS AS (expr)
21593                // TSQL computed column without PERSISTED: AS (expr)
21594                if matches!(
21595                    self.config.dialect,
21596                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive)
21597                ) {
21598                    self.write_keyword("GENERATED ALWAYS AS");
21599                    self.write(" (");
21600                    self.generate_expression(&computed_expr)?;
21601                    self.write(")");
21602                } else if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21603                    self.write_keyword("AS");
21604                    let omit_parens = matches!(computed_expr, Expression::Year(_))
21605                        || matches!(&computed_expr, Expression::Function(f) if f.name.eq_ignore_ascii_case("YEAR"));
21606                    if omit_parens {
21607                        self.write_space();
21608                        self.generate_expression(&computed_expr)?;
21609                    } else {
21610                        self.write(" (");
21611                        self.generate_expression(&computed_expr)?;
21612                        self.write(")");
21613                    }
21614                } else {
21615                    self.write_keyword("AS");
21616                    self.write(" (");
21617                    self.generate_expression(&computed_expr)?;
21618                    self.write(")");
21619                }
21620            }
21621        }
21622        Ok(())
21623    }
21624
21625    /// Generate a GeneratedAsRow constraint inline within a column definition.
21626    /// TSQL temporal: GENERATED ALWAYS AS ROW START|END [HIDDEN]
21627    fn generate_generated_as_row_inline(&mut self, gar: &GeneratedAsRow) -> Result<()> {
21628        self.write_keyword("GENERATED ALWAYS AS ROW ");
21629        if gar.start {
21630            self.write_keyword("START");
21631        } else {
21632            self.write_keyword("END");
21633        }
21634        if gar.hidden {
21635            self.write_space();
21636            self.write_keyword("HIDDEN");
21637        }
21638        Ok(())
21639    }
21640
21641    /// Generate just the SYSTEM_VERSIONING=ON(...) content without WITH() wrapper.
21642    fn generate_system_versioning_content(&mut self, e: &WithSystemVersioningProperty) -> Result<()> {
21643        let mut parts = Vec::new();
21644
21645        if let Some(this) = &e.this {
21646            let mut s = String::from("HISTORY_TABLE=");
21647            let mut gen = Generator::new();
21648            gen.config = self.config.clone();
21649            gen.generate_expression(this)?;
21650            s.push_str(&gen.output);
21651            parts.push(s);
21652        }
21653
21654        if let Some(data_consistency) = &e.data_consistency {
21655            let mut s = String::from("DATA_CONSISTENCY_CHECK=");
21656            let mut gen = Generator::new();
21657            gen.config = self.config.clone();
21658            gen.generate_expression(data_consistency)?;
21659            s.push_str(&gen.output);
21660            parts.push(s);
21661        }
21662
21663        if let Some(retention_period) = &e.retention_period {
21664            let mut s = String::from("HISTORY_RETENTION_PERIOD=");
21665            let mut gen = Generator::new();
21666            gen.config = self.config.clone();
21667            gen.generate_expression(retention_period)?;
21668            s.push_str(&gen.output);
21669            parts.push(s);
21670        }
21671
21672        self.write_keyword("SYSTEM_VERSIONING");
21673        self.write("=");
21674
21675        if !parts.is_empty() {
21676            self.write_keyword("ON");
21677            self.write("(");
21678            self.write(&parts.join(", "));
21679            self.write(")");
21680        } else if e.on.is_some() {
21681            self.write_keyword("ON");
21682        } else {
21683            self.write_keyword("OFF");
21684        }
21685
21686        Ok(())
21687    }
21688
21689    fn generate_conditional_insert(&mut self, e: &ConditionalInsert) -> Result<()> {
21690        // Conditional INSERT for multi-table inserts
21691        // Output: [WHEN cond THEN | ELSE] INTO table [(cols)] [VALUES (...)]
21692        if e.else_.is_some() {
21693            self.write_keyword("ELSE");
21694            self.write_space();
21695        } else if let Some(expression) = &e.expression {
21696            self.write_keyword("WHEN");
21697            self.write_space();
21698            self.generate_expression(expression)?;
21699            self.write_space();
21700            self.write_keyword("THEN");
21701            self.write_space();
21702        }
21703
21704        // Handle Insert expression specially - output "INTO table (cols) VALUES (...)"
21705        // without the "INSERT " prefix
21706        if let Expression::Insert(insert) = e.this.as_ref() {
21707            self.write_keyword("INTO");
21708            self.write_space();
21709            self.generate_table(&insert.table)?;
21710
21711            // Optional column list
21712            if !insert.columns.is_empty() {
21713                self.write(" (");
21714                for (i, col) in insert.columns.iter().enumerate() {
21715                    if i > 0 {
21716                        self.write(", ");
21717                    }
21718                    self.generate_identifier(col)?;
21719                }
21720                self.write(")");
21721            }
21722
21723            // Optional VALUES clause
21724            if !insert.values.is_empty() {
21725                self.write_space();
21726                self.write_keyword("VALUES");
21727                for (row_idx, row) in insert.values.iter().enumerate() {
21728                    if row_idx > 0 {
21729                        self.write(", ");
21730                    }
21731                    self.write(" (");
21732                    for (i, val) in row.iter().enumerate() {
21733                        if i > 0 {
21734                            self.write(", ");
21735                        }
21736                        self.generate_expression(val)?;
21737                    }
21738                    self.write(")");
21739                }
21740            }
21741        } else {
21742            // Fallback for non-Insert expressions
21743            self.generate_expression(&e.this)?;
21744        }
21745        Ok(())
21746    }
21747
21748    fn generate_constraint(&mut self, e: &Constraint) -> Result<()> {
21749        // Python: return f"CONSTRAINT {this} {expressions}"
21750        self.write_keyword("CONSTRAINT");
21751        self.write_space();
21752        self.generate_expression(&e.this)?;
21753        if !e.expressions.is_empty() {
21754            self.write_space();
21755            for (i, expr) in e.expressions.iter().enumerate() {
21756                if i > 0 {
21757                    self.write_space();
21758                }
21759                self.generate_expression(expr)?;
21760            }
21761        }
21762        Ok(())
21763    }
21764
21765    fn generate_convert_timezone(&mut self, e: &ConvertTimezone) -> Result<()> {
21766        // CONVERT_TIMEZONE([source_tz,] target_tz, timestamp)
21767        self.write_keyword("CONVERT_TIMEZONE");
21768        self.write("(");
21769        let mut first = true;
21770        if let Some(source_tz) = &e.source_tz {
21771            self.generate_expression(source_tz)?;
21772            first = false;
21773        }
21774        if let Some(target_tz) = &e.target_tz {
21775            if !first {
21776                self.write(", ");
21777            }
21778            self.generate_expression(target_tz)?;
21779            first = false;
21780        }
21781        if let Some(timestamp) = &e.timestamp {
21782            if !first {
21783                self.write(", ");
21784            }
21785            self.generate_expression(timestamp)?;
21786        }
21787        self.write(")");
21788        Ok(())
21789    }
21790
21791    fn generate_convert_to_charset(&mut self, e: &ConvertToCharset) -> Result<()> {
21792        // CONVERT(this USING dest)
21793        self.write_keyword("CONVERT");
21794        self.write("(");
21795        self.generate_expression(&e.this)?;
21796        if let Some(dest) = &e.dest {
21797            self.write_space();
21798            self.write_keyword("USING");
21799            self.write_space();
21800            self.generate_expression(dest)?;
21801        }
21802        self.write(")");
21803        Ok(())
21804    }
21805
21806    fn generate_copy(&mut self, e: &CopyStmt) -> Result<()> {
21807        self.write_keyword("COPY");
21808        if e.is_into {
21809            self.write_space();
21810            self.write_keyword("INTO");
21811        }
21812        self.write_space();
21813
21814        // Generate target table or query (or stage for COPY INTO @stage)
21815        if let Expression::Literal(Literal::String(s)) = &e.this {
21816            if s.starts_with('@') {
21817                self.write(s);
21818            } else {
21819                self.generate_expression(&e.this)?;
21820            }
21821        } else {
21822            self.generate_expression(&e.this)?;
21823        }
21824
21825        // FROM or TO based on kind
21826        if e.kind {
21827            // kind=true means FROM (loading into table)
21828            if self.config.pretty {
21829                self.write_newline();
21830            } else {
21831                self.write_space();
21832            }
21833            self.write_keyword("FROM");
21834            self.write_space();
21835        } else if !e.files.is_empty() {
21836            // kind=false means TO (exporting)
21837            if self.config.pretty {
21838                self.write_newline();
21839            } else {
21840                self.write_space();
21841            }
21842            self.write_keyword("TO");
21843            self.write_space();
21844        }
21845
21846        // Generate source/destination files
21847        for (i, file) in e.files.iter().enumerate() {
21848            if i > 0 {
21849                self.write_space();
21850            }
21851            // For stage references (strings starting with @), output without quotes
21852            if let Expression::Literal(Literal::String(s)) = file {
21853                if s.starts_with('@') {
21854                    self.write(s);
21855                } else {
21856                    self.generate_expression(file)?;
21857                }
21858            } else if let Expression::Identifier(id) = file {
21859                // Backtick-quoted file path (Databricks style: `s3://link`)
21860                if id.quoted {
21861                    self.write("`");
21862                    self.write(&id.name);
21863                    self.write("`");
21864                } else {
21865                    self.generate_expression(file)?;
21866                }
21867            } else {
21868                self.generate_expression(file)?;
21869            }
21870        }
21871
21872        // Generate credentials if present (Snowflake style - not wrapped in WITH)
21873        if !e.with_wrapped {
21874            if let Some(ref creds) = e.credentials {
21875                if let Some(ref storage) = creds.storage {
21876                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21877                    self.write_keyword("STORAGE_INTEGRATION");
21878                    self.write(" = ");
21879                    self.write(storage);
21880                }
21881                if creds.credentials.is_empty() {
21882                    // Empty credentials: CREDENTIALS = ()
21883                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21884                    self.write_keyword("CREDENTIALS");
21885                    self.write(" = ()");
21886                } else {
21887                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21888                    self.write_keyword("CREDENTIALS");
21889                    // Check if this is Redshift-style (single value with empty key)
21890                    // vs Snowflake-style (multiple key=value pairs)
21891                    if creds.credentials.len() == 1 && creds.credentials[0].0.is_empty() {
21892                        // Redshift style: CREDENTIALS 'value'
21893                        self.write(" '");
21894                        self.write(&creds.credentials[0].1);
21895                        self.write("'");
21896                    } else {
21897                        // Snowflake style: CREDENTIALS = (KEY='value' ...)
21898                        self.write(" = (");
21899                        for (i, (k, v)) in creds.credentials.iter().enumerate() {
21900                            if i > 0 {
21901                                self.write_space();
21902                            }
21903                            self.write(k);
21904                            self.write("='");
21905                            self.write(v);
21906                            self.write("'");
21907                        }
21908                        self.write(")");
21909                    }
21910                }
21911                if let Some(ref encryption) = creds.encryption {
21912                    self.write_space();
21913                    self.write_keyword("ENCRYPTION");
21914                    self.write(" = ");
21915                    self.write(encryption);
21916                }
21917            }
21918        }
21919
21920        // Generate parameters
21921        if !e.params.is_empty() {
21922            if e.with_wrapped {
21923                // DuckDB/PostgreSQL/TSQL WITH (...) format
21924                self.write_space();
21925                self.write_keyword("WITH");
21926                self.write(" (");
21927                for (i, param) in e.params.iter().enumerate() {
21928                    if i > 0 {
21929                        self.write(", ");
21930                    }
21931                    self.generate_copy_param_with_format(param)?;
21932                }
21933                self.write(")");
21934            } else {
21935                // Snowflake/Redshift format: KEY = VALUE or KEY VALUE (space separated, no WITH wrapper)
21936                // For Redshift: IAM_ROLE value, CREDENTIALS 'value', REGION 'value', FORMAT type
21937                // For Snowflake: KEY = VALUE
21938                for param in &e.params {
21939                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21940                    // Preserve original case of parameter name (important for Redshift COPY options)
21941                    self.write(&param.name);
21942                    if let Some(ref value) = param.value {
21943                        // Use = only if it was present in the original (param.eq)
21944                        if param.eq {
21945                            self.write(" = ");
21946                        } else {
21947                            self.write(" ");
21948                        }
21949                        if !param.values.is_empty() {
21950                            self.write("(");
21951                            for (i, v) in param.values.iter().enumerate() {
21952                                if i > 0 {
21953                                    self.write_space();
21954                                }
21955                                self.generate_copy_nested_param(v)?;
21956                            }
21957                            self.write(")");
21958                        } else {
21959                            // For COPY parameter values, output identifiers without quoting
21960                            self.generate_copy_param_value(value)?;
21961                        }
21962                    } else if !param.values.is_empty() {
21963                        // For varlen options like FORMAT_OPTIONS, COPY_OPTIONS - no = before (
21964                        if param.eq {
21965                            self.write(" = (");
21966                        } else {
21967                            self.write(" (");
21968                        }
21969                        // Determine separator for values inside parentheses:
21970                        // - Snowflake FILE_FORMAT = (TYPE=CSV FIELD_DELIMITER='|') → space-separated (has = before parens)
21971                        // - Databricks FORMAT_OPTIONS ('opt1'='true', 'opt2'='test') → comma-separated (no = before parens)
21972                        // - Simple value lists like FILES = ('file1', 'file2') → comma-separated
21973                        let is_key_value_pairs = param.values.first().map_or(false, |v| matches!(v, Expression::Eq(_)));
21974                        let sep = if is_key_value_pairs && param.eq { " " } else { ", " };
21975                        for (i, v) in param.values.iter().enumerate() {
21976                            if i > 0 {
21977                                self.write(sep);
21978                            }
21979                            self.generate_copy_nested_param(v)?;
21980                        }
21981                        self.write(")");
21982                    }
21983                }
21984            }
21985        }
21986
21987        Ok(())
21988    }
21989
21990    /// Generate a COPY parameter in WITH (...) format
21991    /// Handles both KEY = VALUE (TSQL) and KEY VALUE (DuckDB/PostgreSQL) formats
21992    fn generate_copy_param_with_format(&mut self, param: &CopyParameter) -> Result<()> {
21993        self.write_keyword(&param.name);
21994        if !param.values.is_empty() {
21995            // Nested values: CREDENTIAL = (IDENTITY='...', SECRET='...')
21996            self.write(" = (");
21997            for (i, v) in param.values.iter().enumerate() {
21998                if i > 0 {
21999                    self.write(", ");
22000                }
22001                self.generate_copy_nested_param(v)?;
22002            }
22003            self.write(")");
22004        } else if let Some(ref value) = param.value {
22005            if param.eq {
22006                self.write(" = ");
22007            } else {
22008                self.write(" ");
22009            }
22010            self.generate_expression(value)?;
22011        }
22012        Ok(())
22013    }
22014
22015    /// Generate nested parameter for COPY statements (KEY=VALUE without spaces)
22016    fn generate_copy_nested_param(&mut self, expr: &Expression) -> Result<()> {
22017        match expr {
22018            Expression::Eq(eq) => {
22019                // Generate key
22020                match &eq.left {
22021                    Expression::Column(c) => self.write(&c.name.name),
22022                    _ => self.generate_expression(&eq.left)?,
22023                }
22024                self.write("=");
22025                // Generate value
22026                match &eq.right {
22027                    Expression::Literal(Literal::String(s)) => {
22028                        self.write("'");
22029                        self.write(s);
22030                        self.write("'");
22031                    }
22032                    Expression::Tuple(t) => {
22033                        // For lists like NULL_IF=('', 'str1')
22034                        self.write("(");
22035                        if self.config.pretty {
22036                            self.write_newline();
22037                            self.indent_level += 1;
22038                            for (i, item) in t.expressions.iter().enumerate() {
22039                                if i > 0 {
22040                                    self.write(", ");
22041                                }
22042                                self.write_indent();
22043                                self.generate_expression(item)?;
22044                            }
22045                            self.write_newline();
22046                            self.indent_level -= 1;
22047                        } else {
22048                            for (i, item) in t.expressions.iter().enumerate() {
22049                                if i > 0 {
22050                                    self.write(", ");
22051                                }
22052                                self.generate_expression(item)?;
22053                            }
22054                        }
22055                        self.write(")");
22056                    }
22057                    _ => self.generate_expression(&eq.right)?,
22058                }
22059                Ok(())
22060            }
22061            Expression::Column(c) => {
22062                // Standalone keyword like COMPRESSION
22063                self.write(&c.name.name);
22064                Ok(())
22065            }
22066            _ => self.generate_expression(expr),
22067        }
22068    }
22069
22070    /// Generate a COPY parameter value, outputting identifiers/columns without quoting
22071    /// This is needed for Redshift-style COPY params like: IAM_ROLE default, FORMAT orc
22072    fn generate_copy_param_value(&mut self, expr: &Expression) -> Result<()> {
22073        match expr {
22074            Expression::Column(c) => {
22075                // Output identifier, preserving quotes if originally quoted
22076                if c.name.quoted {
22077                    self.write("\"");
22078                    self.write(&c.name.name);
22079                    self.write("\"");
22080                } else {
22081                    self.write(&c.name.name);
22082                }
22083                Ok(())
22084            }
22085            Expression::Identifier(id) => {
22086                // Output identifier, preserving quotes if originally quoted
22087                if id.quoted {
22088                    self.write("\"");
22089                    self.write(&id.name);
22090                    self.write("\"");
22091                } else {
22092                    self.write(&id.name);
22093                }
22094                Ok(())
22095            }
22096            Expression::Literal(Literal::String(s)) => {
22097                // Output string with quotes
22098                self.write("'");
22099                self.write(s);
22100                self.write("'");
22101                Ok(())
22102            }
22103            _ => self.generate_expression(expr),
22104        }
22105    }
22106
22107    fn generate_copy_parameter(&mut self, e: &CopyParameter) -> Result<()> {
22108        self.write_keyword(&e.name);
22109        if let Some(ref value) = e.value {
22110            if e.eq {
22111                self.write(" = ");
22112            } else {
22113                self.write(" ");
22114            }
22115            self.generate_expression(value)?;
22116        }
22117        if !e.values.is_empty() {
22118            if e.eq {
22119                self.write(" = ");
22120            } else {
22121                self.write(" ");
22122            }
22123            self.write("(");
22124            for (i, v) in e.values.iter().enumerate() {
22125                if i > 0 {
22126                    self.write(", ");
22127                }
22128                self.generate_expression(v)?;
22129            }
22130            self.write(")");
22131        }
22132        Ok(())
22133    }
22134
22135    fn generate_corr(&mut self, e: &Corr) -> Result<()> {
22136        // CORR(this, expression)
22137        self.write_keyword("CORR");
22138        self.write("(");
22139        self.generate_expression(&e.this)?;
22140        self.write(", ");
22141        self.generate_expression(&e.expression)?;
22142        self.write(")");
22143        Ok(())
22144    }
22145
22146    fn generate_cosine_distance(&mut self, e: &CosineDistance) -> Result<()> {
22147        // COSINE_DISTANCE(this, expression)
22148        self.write_keyword("COSINE_DISTANCE");
22149        self.write("(");
22150        self.generate_expression(&e.this)?;
22151        self.write(", ");
22152        self.generate_expression(&e.expression)?;
22153        self.write(")");
22154        Ok(())
22155    }
22156
22157    fn generate_covar_pop(&mut self, e: &CovarPop) -> Result<()> {
22158        // COVAR_POP(this, expression)
22159        self.write_keyword("COVAR_POP");
22160        self.write("(");
22161        self.generate_expression(&e.this)?;
22162        self.write(", ");
22163        self.generate_expression(&e.expression)?;
22164        self.write(")");
22165        Ok(())
22166    }
22167
22168    fn generate_covar_samp(&mut self, e: &CovarSamp) -> Result<()> {
22169        // COVAR_SAMP(this, expression)
22170        self.write_keyword("COVAR_SAMP");
22171        self.write("(");
22172        self.generate_expression(&e.this)?;
22173        self.write(", ");
22174        self.generate_expression(&e.expression)?;
22175        self.write(")");
22176        Ok(())
22177    }
22178
22179    fn generate_credentials(&mut self, e: &Credentials) -> Result<()> {
22180        // CREDENTIALS (key1='value1', key2='value2')
22181        self.write_keyword("CREDENTIALS");
22182        self.write(" (");
22183        for (i, (key, value)) in e.credentials.iter().enumerate() {
22184            if i > 0 {
22185                self.write(", ");
22186            }
22187            self.write(key);
22188            self.write("='");
22189            self.write(value);
22190            self.write("'");
22191        }
22192        self.write(")");
22193        Ok(())
22194    }
22195
22196    fn generate_credentials_property(&mut self, e: &CredentialsProperty) -> Result<()> {
22197        // CREDENTIALS=(expressions)
22198        self.write_keyword("CREDENTIALS");
22199        self.write("=(");
22200        for (i, expr) in e.expressions.iter().enumerate() {
22201            if i > 0 {
22202                self.write(", ");
22203            }
22204            self.generate_expression(expr)?;
22205        }
22206        self.write(")");
22207        Ok(())
22208    }
22209
22210    fn generate_cte(&mut self, e: &Cte) -> Result<()> {
22211        use crate::dialects::DialectType;
22212
22213        // Python: return f"{alias_sql}{key_expressions} AS {materialized or ''}{self.wrap(expression)}"
22214        // Output: alias [(col1, col2, ...)] AS [MATERIALIZED|NOT MATERIALIZED] (subquery)
22215        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) && !e.alias_first {
22216            self.generate_expression(&e.this)?;
22217            self.write_space();
22218            self.write_keyword("AS");
22219            self.write_space();
22220            self.generate_identifier(&e.alias)?;
22221            return Ok(());
22222        }
22223        self.write(&e.alias.name);
22224
22225        // BigQuery doesn't support column aliases in CTE definitions
22226        let skip_cte_columns = matches!(self.config.dialect, Some(DialectType::BigQuery));
22227
22228        if !e.columns.is_empty() && !skip_cte_columns {
22229            self.write("(");
22230            for (i, col) in e.columns.iter().enumerate() {
22231                if i > 0 {
22232                    self.write(", ");
22233                }
22234                self.write(&col.name);
22235            }
22236            self.write(")");
22237        }
22238        // USING KEY (columns) for DuckDB recursive CTEs
22239        if !e.key_expressions.is_empty() {
22240            self.write_space();
22241            self.write_keyword("USING KEY");
22242            self.write(" (");
22243            for (i, key) in e.key_expressions.iter().enumerate() {
22244                if i > 0 {
22245                    self.write(", ");
22246                }
22247                self.write(&key.name);
22248            }
22249            self.write(")");
22250        }
22251        self.write_space();
22252        self.write_keyword("AS");
22253        self.write_space();
22254        if let Some(materialized) = e.materialized {
22255            if materialized {
22256                self.write_keyword("MATERIALIZED");
22257            } else {
22258                self.write_keyword("NOT MATERIALIZED");
22259            }
22260            self.write_space();
22261        }
22262        self.write("(");
22263        self.generate_expression(&e.this)?;
22264        self.write(")");
22265        Ok(())
22266    }
22267
22268    fn generate_cube(&mut self, e: &Cube) -> Result<()> {
22269        // Python: return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
22270        if e.expressions.is_empty() {
22271            self.write_keyword("WITH CUBE");
22272        } else {
22273            self.write_keyword("CUBE");
22274            self.write("(");
22275            for (i, expr) in e.expressions.iter().enumerate() {
22276                if i > 0 {
22277                    self.write(", ");
22278                }
22279                self.generate_expression(expr)?;
22280            }
22281            self.write(")");
22282        }
22283        Ok(())
22284    }
22285
22286    fn generate_current_datetime(&mut self, e: &CurrentDatetime) -> Result<()> {
22287        // CURRENT_DATETIME or CURRENT_DATETIME(timezone)
22288        self.write_keyword("CURRENT_DATETIME");
22289        if let Some(this) = &e.this {
22290            self.write("(");
22291            self.generate_expression(this)?;
22292            self.write(")");
22293        }
22294        Ok(())
22295    }
22296
22297    fn generate_current_schema(&mut self, _e: &CurrentSchema) -> Result<()> {
22298        // CURRENT_SCHEMA - no arguments
22299        self.write_keyword("CURRENT_SCHEMA");
22300        Ok(())
22301    }
22302
22303    fn generate_current_schemas(&mut self, e: &CurrentSchemas) -> Result<()> {
22304        // CURRENT_SCHEMAS(include_implicit)
22305        self.write_keyword("CURRENT_SCHEMAS");
22306        self.write("(");
22307        if let Some(this) = &e.this {
22308            self.generate_expression(this)?;
22309        }
22310        self.write(")");
22311        Ok(())
22312    }
22313
22314    fn generate_current_user(&mut self, e: &CurrentUser) -> Result<()> {
22315        // CURRENT_USER or CURRENT_USER()
22316        self.write_keyword("CURRENT_USER");
22317        // Some dialects always need parens: Snowflake, Spark, Hive, DuckDB, BigQuery, MySQL, Databricks
22318        let needs_parens = e.this.is_some() || matches!(
22319            self.config.dialect,
22320            Some(DialectType::Snowflake) | Some(DialectType::Spark)
22321            | Some(DialectType::Hive) | Some(DialectType::DuckDB) | Some(DialectType::BigQuery)
22322            | Some(DialectType::MySQL) | Some(DialectType::Databricks)
22323        );
22324        if needs_parens {
22325            self.write("()");
22326        }
22327        Ok(())
22328    }
22329
22330    fn generate_d_pipe(&mut self, e: &DPipe) -> Result<()> {
22331        // In Solr, || is OR, not string concatenation (DPIPE_IS_STRING_CONCAT = False)
22332        if self.config.dialect == Some(DialectType::Solr) {
22333            self.generate_expression(&e.this)?;
22334            self.write(" ");
22335            self.write_keyword("OR");
22336            self.write(" ");
22337            self.generate_expression(&e.expression)?;
22338        } else {
22339            // String concatenation: this || expression
22340            self.generate_expression(&e.this)?;
22341            self.write(" || ");
22342            self.generate_expression(&e.expression)?;
22343        }
22344        Ok(())
22345    }
22346
22347    fn generate_data_blocksize_property(&mut self, e: &DataBlocksizeProperty) -> Result<()> {
22348        // DATABLOCKSIZE=... (Teradata)
22349        self.write_keyword("DATABLOCKSIZE");
22350        self.write("=");
22351        if let Some(size) = e.size {
22352            self.write(&size.to_string());
22353            if let Some(units) = &e.units {
22354                self.write_space();
22355                self.generate_expression(units)?;
22356            }
22357        } else if e.minimum.is_some() {
22358            self.write_keyword("MINIMUM");
22359        } else if e.maximum.is_some() {
22360            self.write_keyword("MAXIMUM");
22361        } else if e.default.is_some() {
22362            self.write_keyword("DEFAULT");
22363        }
22364        Ok(())
22365    }
22366
22367    fn generate_data_deletion_property(&mut self, e: &DataDeletionProperty) -> Result<()> {
22368        // DATA_DELETION=ON or DATA_DELETION=OFF or DATA_DELETION=ON(FILTER_COLUMN=col, RETENTION_PERIOD=...)
22369        self.write_keyword("DATA_DELETION");
22370        self.write("=");
22371
22372        let is_on = matches!(&*e.on, Expression::Boolean(BooleanLiteral { value: true }));
22373        let has_options = e.filter_column.is_some() || e.retention_period.is_some();
22374
22375        if is_on {
22376            self.write_keyword("ON");
22377            if has_options {
22378                self.write("(");
22379                let mut first = true;
22380                if let Some(filter_column) = &e.filter_column {
22381                    self.write_keyword("FILTER_COLUMN");
22382                    self.write("=");
22383                    self.generate_expression(filter_column)?;
22384                    first = false;
22385                }
22386                if let Some(retention_period) = &e.retention_period {
22387                    if !first {
22388                        self.write(", ");
22389                    }
22390                    self.write_keyword("RETENTION_PERIOD");
22391                    self.write("=");
22392                    self.generate_expression(retention_period)?;
22393                }
22394                self.write(")");
22395            }
22396        } else {
22397            self.write_keyword("OFF");
22398        }
22399        Ok(())
22400    }
22401
22402    /// Generate a Date function expression
22403    /// For Exasol: {d'value'} -> TO_DATE('value')
22404    /// For other dialects: DATE('value')
22405    fn generate_date_func(&mut self, e: &UnaryFunc) -> Result<()> {
22406        use crate::dialects::DialectType;
22407        use crate::expressions::Literal;
22408
22409        match self.config.dialect {
22410            // Exasol uses TO_DATE for Date expressions
22411            Some(DialectType::Exasol) => {
22412                self.write_keyword("TO_DATE");
22413                self.write("(");
22414                // Extract the string value from the expression if it's a string literal
22415                match &e.this {
22416                    Expression::Literal(Literal::String(s)) => {
22417                        self.write("'");
22418                        self.write(s);
22419                        self.write("'");
22420                    }
22421                    _ => {
22422                        self.generate_expression(&e.this)?;
22423                    }
22424                }
22425                self.write(")");
22426            }
22427            // Standard: DATE(value)
22428            _ => {
22429                self.write_keyword("DATE");
22430                self.write("(");
22431                self.generate_expression(&e.this)?;
22432                self.write(")");
22433            }
22434        }
22435        Ok(())
22436    }
22437
22438    fn generate_date_bin(&mut self, e: &DateBin) -> Result<()> {
22439        // DATE_BIN(interval, timestamp[, origin])
22440        self.write_keyword("DATE_BIN");
22441        self.write("(");
22442        self.generate_expression(&e.this)?;
22443        self.write(", ");
22444        self.generate_expression(&e.expression)?;
22445        if let Some(origin) = &e.origin {
22446            self.write(", ");
22447            self.generate_expression(origin)?;
22448        }
22449        self.write(")");
22450        Ok(())
22451    }
22452
22453    fn generate_date_format_column_constraint(&mut self, e: &DateFormatColumnConstraint) -> Result<()> {
22454        // FORMAT 'format_string' (Teradata)
22455        self.write_keyword("FORMAT");
22456        self.write_space();
22457        self.generate_expression(&e.this)?;
22458        Ok(())
22459    }
22460
22461    fn generate_date_from_parts(&mut self, e: &DateFromParts) -> Result<()> {
22462        // DATE_FROM_PARTS(year, month, day) or DATEFROMPARTS(year, month, day)
22463        self.write_keyword("DATE_FROM_PARTS");
22464        self.write("(");
22465        let mut first = true;
22466        if let Some(year) = &e.year {
22467            self.generate_expression(year)?;
22468            first = false;
22469        }
22470        if let Some(month) = &e.month {
22471            if !first {
22472                self.write(", ");
22473            }
22474            self.generate_expression(month)?;
22475            first = false;
22476        }
22477        if let Some(day) = &e.day {
22478            if !first {
22479                self.write(", ");
22480            }
22481            self.generate_expression(day)?;
22482        }
22483        self.write(")");
22484        Ok(())
22485    }
22486
22487    fn generate_datetime(&mut self, e: &Datetime) -> Result<()> {
22488        // DATETIME(this) or DATETIME(this, expression)
22489        self.write_keyword("DATETIME");
22490        self.write("(");
22491        self.generate_expression(&e.this)?;
22492        if let Some(expr) = &e.expression {
22493            self.write(", ");
22494            self.generate_expression(expr)?;
22495        }
22496        self.write(")");
22497        Ok(())
22498    }
22499
22500    fn generate_datetime_add(&mut self, e: &DatetimeAdd) -> Result<()> {
22501        // DATETIME_ADD(this, expression, unit)
22502        self.write_keyword("DATETIME_ADD");
22503        self.write("(");
22504        self.generate_expression(&e.this)?;
22505        self.write(", ");
22506        self.generate_expression(&e.expression)?;
22507        if let Some(unit) = &e.unit {
22508            self.write(", ");
22509            self.write_keyword(unit);
22510        }
22511        self.write(")");
22512        Ok(())
22513    }
22514
22515    fn generate_datetime_diff(&mut self, e: &DatetimeDiff) -> Result<()> {
22516        // DATETIME_DIFF(this, expression, unit)
22517        self.write_keyword("DATETIME_DIFF");
22518        self.write("(");
22519        self.generate_expression(&e.this)?;
22520        self.write(", ");
22521        self.generate_expression(&e.expression)?;
22522        if let Some(unit) = &e.unit {
22523            self.write(", ");
22524            self.write_keyword(unit);
22525        }
22526        self.write(")");
22527        Ok(())
22528    }
22529
22530    fn generate_datetime_sub(&mut self, e: &DatetimeSub) -> Result<()> {
22531        // DATETIME_SUB(this, expression, unit)
22532        self.write_keyword("DATETIME_SUB");
22533        self.write("(");
22534        self.generate_expression(&e.this)?;
22535        self.write(", ");
22536        self.generate_expression(&e.expression)?;
22537        if let Some(unit) = &e.unit {
22538            self.write(", ");
22539            self.write_keyword(unit);
22540        }
22541        self.write(")");
22542        Ok(())
22543    }
22544
22545    fn generate_datetime_trunc(&mut self, e: &DatetimeTrunc) -> Result<()> {
22546        // DATETIME_TRUNC(this, unit, zone)
22547        self.write_keyword("DATETIME_TRUNC");
22548        self.write("(");
22549        self.generate_expression(&e.this)?;
22550        self.write(", ");
22551        self.write_keyword(&e.unit);
22552        if let Some(zone) = &e.zone {
22553            self.write(", ");
22554            self.generate_expression(zone)?;
22555        }
22556        self.write(")");
22557        Ok(())
22558    }
22559
22560    fn generate_dayname(&mut self, e: &Dayname) -> Result<()> {
22561        // DAYNAME(this)
22562        self.write_keyword("DAYNAME");
22563        self.write("(");
22564        self.generate_expression(&e.this)?;
22565        self.write(")");
22566        Ok(())
22567    }
22568
22569    fn generate_declare(&mut self, e: &Declare) -> Result<()> {
22570        // DECLARE var1 AS type1, var2 AS type2, ...
22571        self.write_keyword("DECLARE");
22572        self.write_space();
22573        for (i, expr) in e.expressions.iter().enumerate() {
22574            if i > 0 {
22575                self.write(", ");
22576            }
22577            self.generate_expression(expr)?;
22578        }
22579        Ok(())
22580    }
22581
22582    fn generate_declare_item(&mut self, e: &DeclareItem) -> Result<()> {
22583        use crate::dialects::DialectType;
22584
22585        // variable TYPE [DEFAULT default]
22586        self.generate_expression(&e.this)?;
22587        // BigQuery multi-variable: DECLARE X, Y, Z INT64
22588        for name in &e.additional_names {
22589            self.write(", ");
22590            self.generate_expression(name)?;
22591        }
22592        if let Some(kind) = &e.kind {
22593            self.write_space();
22594            // BigQuery uses: DECLARE x INT64 DEFAULT value (no AS)
22595            // TSQL: Always includes AS (normalization)
22596            // Others: Include AS if present in original
22597            match self.config.dialect {
22598                Some(DialectType::BigQuery) => {
22599                    self.write(kind);
22600                }
22601                Some(DialectType::TSQL) => {
22602                    // TSQL: Check for complex TABLE constraints that should be passed through unchanged
22603                    // Python sqlglot falls back to Command for TABLE declarations with CLUSTERED,
22604                    // NONCLUSTERED, or INDEX constraints
22605                    let is_complex_table = kind.starts_with("TABLE") &&
22606                        (kind.contains("CLUSTERED") || kind.contains("INDEX"));
22607
22608                    if is_complex_table {
22609                        // Complex TABLE declarations: preserve as-is (no AS, no INT normalization)
22610                        self.write(kind);
22611                    } else {
22612                        // Simple declarations: add AS (except for CURSOR) and normalize INT
22613                        if !kind.starts_with("CURSOR") {
22614                            self.write_keyword("AS");
22615                            self.write_space();
22616                        }
22617                        // Normalize INT to INTEGER for TSQL DECLARE statements
22618                        if kind == "INT" {
22619                            self.write("INTEGER");
22620                        } else if kind.starts_with("TABLE") {
22621                            // Normalize INT to INTEGER inside TABLE column definitions
22622                            let normalized = kind
22623                                .replace(" INT ", " INTEGER ")
22624                                .replace(" INT,", " INTEGER,")
22625                                .replace(" INT)", " INTEGER)")
22626                                .replace("(INT ", "(INTEGER ");
22627                            self.write(&normalized);
22628                        } else {
22629                            self.write(kind);
22630                        }
22631                    }
22632                }
22633                _ => {
22634                    if e.has_as {
22635                        self.write_keyword("AS");
22636                        self.write_space();
22637                    }
22638                    self.write(kind);
22639                }
22640            }
22641        }
22642        if let Some(default) = &e.default {
22643            // BigQuery uses DEFAULT, others use =
22644            match self.config.dialect {
22645                Some(DialectType::BigQuery) => {
22646                    self.write_space();
22647                    self.write_keyword("DEFAULT");
22648                    self.write_space();
22649                }
22650                _ => {
22651                    self.write(" = ");
22652                }
22653            }
22654            self.generate_expression(default)?;
22655        }
22656        Ok(())
22657    }
22658
22659    fn generate_decode_case(&mut self, e: &DecodeCase) -> Result<()> {
22660        // DECODE(expr, search1, result1, search2, result2, ..., default)
22661        self.write_keyword("DECODE");
22662        self.write("(");
22663        for (i, expr) in e.expressions.iter().enumerate() {
22664            if i > 0 {
22665                self.write(", ");
22666            }
22667            self.generate_expression(expr)?;
22668        }
22669        self.write(")");
22670        Ok(())
22671    }
22672
22673    fn generate_decompress_binary(&mut self, e: &DecompressBinary) -> Result<()> {
22674        // DECOMPRESS(expr, 'method')
22675        self.write_keyword("DECOMPRESS");
22676        self.write("(");
22677        self.generate_expression(&e.this)?;
22678        self.write(", '");
22679        self.write(&e.method);
22680        self.write("')");
22681        Ok(())
22682    }
22683
22684    fn generate_decompress_string(&mut self, e: &DecompressString) -> Result<()> {
22685        // DECOMPRESS(expr, 'method')
22686        self.write_keyword("DECOMPRESS");
22687        self.write("(");
22688        self.generate_expression(&e.this)?;
22689        self.write(", '");
22690        self.write(&e.method);
22691        self.write("')");
22692        Ok(())
22693    }
22694
22695    fn generate_decrypt(&mut self, e: &Decrypt) -> Result<()> {
22696        // DECRYPT(value, passphrase [, aad [, algorithm]])
22697        self.write_keyword("DECRYPT");
22698        self.write("(");
22699        self.generate_expression(&e.this)?;
22700        if let Some(passphrase) = &e.passphrase {
22701            self.write(", ");
22702            self.generate_expression(passphrase)?;
22703        }
22704        if let Some(aad) = &e.aad {
22705            self.write(", ");
22706            self.generate_expression(aad)?;
22707        }
22708        if let Some(method) = &e.encryption_method {
22709            self.write(", ");
22710            self.generate_expression(method)?;
22711        }
22712        self.write(")");
22713        Ok(())
22714    }
22715
22716    fn generate_decrypt_raw(&mut self, e: &DecryptRaw) -> Result<()> {
22717        // DECRYPT_RAW(value, key [, iv [, aad [, algorithm]]])
22718        self.write_keyword("DECRYPT_RAW");
22719        self.write("(");
22720        self.generate_expression(&e.this)?;
22721        if let Some(key) = &e.key {
22722            self.write(", ");
22723            self.generate_expression(key)?;
22724        }
22725        if let Some(iv) = &e.iv {
22726            self.write(", ");
22727            self.generate_expression(iv)?;
22728        }
22729        if let Some(aad) = &e.aad {
22730            self.write(", ");
22731            self.generate_expression(aad)?;
22732        }
22733        if let Some(method) = &e.encryption_method {
22734            self.write(", ");
22735            self.generate_expression(method)?;
22736        }
22737        self.write(")");
22738        Ok(())
22739    }
22740
22741    fn generate_definer_property(&mut self, e: &DefinerProperty) -> Result<()> {
22742        // DEFINER = user
22743        self.write_keyword("DEFINER");
22744        self.write(" = ");
22745        self.generate_expression(&e.this)?;
22746        Ok(())
22747    }
22748
22749    fn generate_detach(&mut self, e: &Detach) -> Result<()> {
22750        // Python: DETACH[DATABASE IF EXISTS] this
22751        self.write_keyword("DETACH");
22752        if e.exists {
22753            self.write_keyword(" DATABASE IF EXISTS");
22754        }
22755        self.write_space();
22756        self.generate_expression(&e.this)?;
22757        Ok(())
22758    }
22759
22760    fn generate_dict_property(&mut self, e: &DictProperty) -> Result<()> {
22761        let property_name = match e.this.as_ref() {
22762            Expression::Identifier(id) => id.name.as_str(),
22763            Expression::Var(v) => v.this.as_str(),
22764            _ => "DICTIONARY",
22765        };
22766        self.write_keyword(property_name);
22767        self.write("(");
22768        self.write(&e.kind);
22769        if let Some(settings) = &e.settings {
22770            self.write("(");
22771            if let Expression::Tuple(t) = settings.as_ref() {
22772                if self.config.pretty && !t.expressions.is_empty() {
22773                    self.write_newline();
22774                    self.indent_level += 1;
22775                    for (i, pair) in t.expressions.iter().enumerate() {
22776                        if i > 0 {
22777                            self.write(",");
22778                            self.write_newline();
22779                        }
22780                        self.write_indent();
22781                        if let Expression::Tuple(pair_tuple) = pair {
22782                            if let Some(k) = pair_tuple.expressions.first() {
22783                                self.generate_expression(k)?;
22784                            }
22785                            if let Some(v) = pair_tuple.expressions.get(1) {
22786                                self.write(" ");
22787                                self.generate_expression(v)?;
22788                            }
22789                        } else {
22790                            self.generate_expression(pair)?;
22791                        }
22792                    }
22793                    self.indent_level -= 1;
22794                    self.write_newline();
22795                    self.write_indent();
22796                } else {
22797                    for (i, pair) in t.expressions.iter().enumerate() {
22798                        if i > 0 {
22799                            self.write(", ");
22800                        }
22801                        if let Expression::Tuple(pair_tuple) = pair {
22802                            if let Some(k) = pair_tuple.expressions.first() {
22803                                self.generate_expression(k)?;
22804                            }
22805                            if let Some(v) = pair_tuple.expressions.get(1) {
22806                                self.write(" ");
22807                                self.generate_expression(v)?;
22808                            }
22809                        } else {
22810                            self.generate_expression(pair)?;
22811                        }
22812                    }
22813                }
22814            } else {
22815                self.generate_expression(settings)?;
22816            }
22817            self.write(")");
22818        } else if property_name.eq_ignore_ascii_case("LAYOUT") {
22819            self.write("()");
22820        }
22821        self.write(")");
22822        Ok(())
22823    }
22824
22825    fn generate_dict_range(&mut self, e: &DictRange) -> Result<()> {
22826        let property_name = match e.this.as_ref() {
22827            Expression::Identifier(id) => id.name.as_str(),
22828            Expression::Var(v) => v.this.as_str(),
22829            _ => "RANGE",
22830        };
22831        self.write_keyword(property_name);
22832        self.write("(");
22833        if let Some(min) = &e.min {
22834            self.write_keyword("MIN");
22835            self.write_space();
22836            self.generate_expression(min)?;
22837        }
22838        if let Some(max) = &e.max {
22839            self.write_space();
22840            self.write_keyword("MAX");
22841            self.write_space();
22842            self.generate_expression(max)?;
22843        }
22844        self.write(")");
22845        Ok(())
22846    }
22847
22848    fn generate_directory(&mut self, e: &Directory) -> Result<()> {
22849        // Python: {local}DIRECTORY {this}{row_format}
22850        if e.local.is_some() {
22851            self.write_keyword("LOCAL ");
22852        }
22853        self.write_keyword("DIRECTORY");
22854        self.write_space();
22855        self.generate_expression(&e.this)?;
22856        if let Some(row_format) = &e.row_format {
22857            self.write_space();
22858            self.generate_expression(row_format)?;
22859        }
22860        Ok(())
22861    }
22862
22863    fn generate_dist_key_property(&mut self, e: &DistKeyProperty) -> Result<()> {
22864        // Redshift: DISTKEY(column)
22865        self.write_keyword("DISTKEY");
22866        self.write("(");
22867        self.generate_expression(&e.this)?;
22868        self.write(")");
22869        Ok(())
22870    }
22871
22872    fn generate_dist_style_property(&mut self, e: &DistStyleProperty) -> Result<()> {
22873        // Redshift: DISTSTYLE KEY|ALL|EVEN|AUTO
22874        self.write_keyword("DISTSTYLE");
22875        self.write_space();
22876        self.generate_expression(&e.this)?;
22877        Ok(())
22878    }
22879
22880    fn generate_distribute_by(&mut self, e: &DistributeBy) -> Result<()> {
22881        // Python: "DISTRIBUTE BY" expressions
22882        self.write_keyword("DISTRIBUTE BY");
22883        self.write_space();
22884        for (i, expr) in e.expressions.iter().enumerate() {
22885            if i > 0 {
22886                self.write(", ");
22887            }
22888            self.generate_expression(expr)?;
22889        }
22890        Ok(())
22891    }
22892
22893    fn generate_distributed_by_property(&mut self, e: &DistributedByProperty) -> Result<()> {
22894        // Python: DISTRIBUTED BY kind (expressions) BUCKETS buckets order
22895        self.write_keyword("DISTRIBUTED BY");
22896        self.write_space();
22897        self.write(&e.kind);
22898        if !e.expressions.is_empty() {
22899            self.write(" (");
22900            for (i, expr) in e.expressions.iter().enumerate() {
22901                if i > 0 {
22902                    self.write(", ");
22903                }
22904                self.generate_expression(expr)?;
22905            }
22906            self.write(")");
22907        }
22908        if let Some(buckets) = &e.buckets {
22909            self.write_space();
22910            self.write_keyword("BUCKETS");
22911            self.write_space();
22912            self.generate_expression(buckets)?;
22913        }
22914        if let Some(order) = &e.order {
22915            self.write_space();
22916            self.generate_expression(order)?;
22917        }
22918        Ok(())
22919    }
22920
22921    fn generate_dot_product(&mut self, e: &DotProduct) -> Result<()> {
22922        // DOT_PRODUCT(vector1, vector2)
22923        self.write_keyword("DOT_PRODUCT");
22924        self.write("(");
22925        self.generate_expression(&e.this)?;
22926        self.write(", ");
22927        self.generate_expression(&e.expression)?;
22928        self.write(")");
22929        Ok(())
22930    }
22931
22932    fn generate_drop_partition(&mut self, e: &DropPartition) -> Result<()> {
22933        // Python: DROP{IF EXISTS }expressions
22934        self.write_keyword("DROP");
22935        if e.exists {
22936            self.write_keyword(" IF EXISTS ");
22937        } else {
22938            self.write_space();
22939        }
22940        for (i, expr) in e.expressions.iter().enumerate() {
22941            if i > 0 {
22942                self.write(", ");
22943            }
22944            self.generate_expression(expr)?;
22945        }
22946        Ok(())
22947    }
22948
22949    fn generate_duplicate_key_property(&mut self, e: &DuplicateKeyProperty) -> Result<()> {
22950        // Python: DUPLICATE KEY (expressions)
22951        self.write_keyword("DUPLICATE KEY");
22952        self.write(" (");
22953        for (i, expr) in e.expressions.iter().enumerate() {
22954            if i > 0 {
22955                self.write(", ");
22956            }
22957            self.generate_expression(expr)?;
22958        }
22959        self.write(")");
22960        Ok(())
22961    }
22962
22963    fn generate_elt(&mut self, e: &Elt) -> Result<()> {
22964        // ELT(index, str1, str2, ...)
22965        self.write_keyword("ELT");
22966        self.write("(");
22967        self.generate_expression(&e.this)?;
22968        for expr in &e.expressions {
22969            self.write(", ");
22970            self.generate_expression(expr)?;
22971        }
22972        self.write(")");
22973        Ok(())
22974    }
22975
22976    fn generate_encode(&mut self, e: &Encode) -> Result<()> {
22977        // ENCODE(string, charset)
22978        self.write_keyword("ENCODE");
22979        self.write("(");
22980        self.generate_expression(&e.this)?;
22981        if let Some(charset) = &e.charset {
22982            self.write(", ");
22983            self.generate_expression(charset)?;
22984        }
22985        self.write(")");
22986        Ok(())
22987    }
22988
22989    fn generate_encode_property(&mut self, e: &EncodeProperty) -> Result<()> {
22990        // Python: [KEY ]ENCODE this [properties]
22991        if e.key.is_some() {
22992            self.write_keyword("KEY ");
22993        }
22994        self.write_keyword("ENCODE");
22995        self.write_space();
22996        self.generate_expression(&e.this)?;
22997        if !e.properties.is_empty() {
22998            self.write(" (");
22999            for (i, prop) in e.properties.iter().enumerate() {
23000                if i > 0 {
23001                    self.write(", ");
23002                }
23003                self.generate_expression(prop)?;
23004            }
23005            self.write(")");
23006        }
23007        Ok(())
23008    }
23009
23010    fn generate_encrypt(&mut self, e: &Encrypt) -> Result<()> {
23011        // ENCRYPT(value, passphrase [, aad [, algorithm]])
23012        self.write_keyword("ENCRYPT");
23013        self.write("(");
23014        self.generate_expression(&e.this)?;
23015        if let Some(passphrase) = &e.passphrase {
23016            self.write(", ");
23017            self.generate_expression(passphrase)?;
23018        }
23019        if let Some(aad) = &e.aad {
23020            self.write(", ");
23021            self.generate_expression(aad)?;
23022        }
23023        if let Some(method) = &e.encryption_method {
23024            self.write(", ");
23025            self.generate_expression(method)?;
23026        }
23027        self.write(")");
23028        Ok(())
23029    }
23030
23031    fn generate_encrypt_raw(&mut self, e: &EncryptRaw) -> Result<()> {
23032        // ENCRYPT_RAW(value, key [, iv [, aad [, algorithm]]])
23033        self.write_keyword("ENCRYPT_RAW");
23034        self.write("(");
23035        self.generate_expression(&e.this)?;
23036        if let Some(key) = &e.key {
23037            self.write(", ");
23038            self.generate_expression(key)?;
23039        }
23040        if let Some(iv) = &e.iv {
23041            self.write(", ");
23042            self.generate_expression(iv)?;
23043        }
23044        if let Some(aad) = &e.aad {
23045            self.write(", ");
23046            self.generate_expression(aad)?;
23047        }
23048        if let Some(method) = &e.encryption_method {
23049            self.write(", ");
23050            self.generate_expression(method)?;
23051        }
23052        self.write(")");
23053        Ok(())
23054    }
23055
23056    fn generate_engine_property(&mut self, e: &EngineProperty) -> Result<()> {
23057        // MySQL: ENGINE = InnoDB
23058        self.write_keyword("ENGINE");
23059        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
23060            self.write("=");
23061        } else {
23062            self.write(" = ");
23063        }
23064        self.generate_expression(&e.this)?;
23065        Ok(())
23066    }
23067
23068    fn generate_enviroment_property(&mut self, e: &EnviromentProperty) -> Result<()> {
23069        // ENVIRONMENT (expressions)
23070        self.write_keyword("ENVIRONMENT");
23071        self.write(" (");
23072        for (i, expr) in e.expressions.iter().enumerate() {
23073            if i > 0 {
23074                self.write(", ");
23075            }
23076            self.generate_expression(expr)?;
23077        }
23078        self.write(")");
23079        Ok(())
23080    }
23081
23082    fn generate_ephemeral_column_constraint(&mut self, e: &EphemeralColumnConstraint) -> Result<()> {
23083        // MySQL: EPHEMERAL [expr]
23084        self.write_keyword("EPHEMERAL");
23085        if let Some(this) = &e.this {
23086            self.write_space();
23087            self.generate_expression(this)?;
23088        }
23089        Ok(())
23090    }
23091
23092    fn generate_equal_null(&mut self, e: &EqualNull) -> Result<()> {
23093        // Snowflake: EQUAL_NULL(a, b)
23094        self.write_keyword("EQUAL_NULL");
23095        self.write("(");
23096        self.generate_expression(&e.this)?;
23097        self.write(", ");
23098        self.generate_expression(&e.expression)?;
23099        self.write(")");
23100        Ok(())
23101    }
23102
23103    fn generate_euclidean_distance(&mut self, e: &EuclideanDistance) -> Result<()> {
23104        use crate::dialects::DialectType;
23105
23106        // PostgreSQL uses <-> operator syntax
23107        match self.config.dialect {
23108            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
23109                self.generate_expression(&e.this)?;
23110                self.write(" <-> ");
23111                self.generate_expression(&e.expression)?;
23112            }
23113            _ => {
23114                // Other dialects use EUCLIDEAN_DISTANCE function
23115                self.write_keyword("EUCLIDEAN_DISTANCE");
23116                self.write("(");
23117                self.generate_expression(&e.this)?;
23118                self.write(", ");
23119                self.generate_expression(&e.expression)?;
23120                self.write(")");
23121            }
23122        }
23123        Ok(())
23124    }
23125
23126    fn generate_execute_as_property(&mut self, e: &ExecuteAsProperty) -> Result<()> {
23127        // EXECUTE AS CALLER|OWNER|user
23128        self.write_keyword("EXECUTE AS");
23129        self.write_space();
23130        self.generate_expression(&e.this)?;
23131        Ok(())
23132    }
23133
23134    fn generate_export(&mut self, e: &Export) -> Result<()> {
23135        // BigQuery: EXPORT DATA [WITH CONNECTION connection] OPTIONS (...) AS query
23136        self.write_keyword("EXPORT DATA");
23137        if let Some(connection) = &e.connection {
23138            self.write_space();
23139            self.write_keyword("WITH CONNECTION");
23140            self.write_space();
23141            self.generate_expression(connection)?;
23142        }
23143        if !e.options.is_empty() {
23144            self.write_space();
23145            self.generate_options_clause(&e.options)?;
23146        }
23147        self.write_space();
23148        self.write_keyword("AS");
23149        self.write_space();
23150        self.generate_expression(&e.this)?;
23151        Ok(())
23152    }
23153
23154    fn generate_external_property(&mut self, e: &ExternalProperty) -> Result<()> {
23155        // EXTERNAL [this]
23156        self.write_keyword("EXTERNAL");
23157        if let Some(this) = &e.this {
23158            self.write_space();
23159            self.generate_expression(this)?;
23160        }
23161        Ok(())
23162    }
23163
23164    fn generate_fallback_property(&mut self, e: &FallbackProperty) -> Result<()> {
23165        // Python: {no}FALLBACK{protection}
23166        if e.no.is_some() {
23167            self.write_keyword("NO ");
23168        }
23169        self.write_keyword("FALLBACK");
23170        if e.protection.is_some() {
23171            self.write_keyword(" PROTECTION");
23172        }
23173        Ok(())
23174    }
23175
23176    fn generate_farm_fingerprint(&mut self, e: &FarmFingerprint) -> Result<()> {
23177        // BigQuery: FARM_FINGERPRINT(value)
23178        self.write_keyword("FARM_FINGERPRINT");
23179        self.write("(");
23180        for (i, expr) in e.expressions.iter().enumerate() {
23181            if i > 0 {
23182                self.write(", ");
23183            }
23184            self.generate_expression(expr)?;
23185        }
23186        self.write(")");
23187        Ok(())
23188    }
23189
23190    fn generate_features_at_time(&mut self, e: &FeaturesAtTime) -> Result<()> {
23191        // BigQuery ML: FEATURES_AT_TIME(feature_view, time, [num_rows], [ignore_feature_nulls])
23192        self.write_keyword("FEATURES_AT_TIME");
23193        self.write("(");
23194        self.generate_expression(&e.this)?;
23195        if let Some(time) = &e.time {
23196            self.write(", ");
23197            self.generate_expression(time)?;
23198        }
23199        if let Some(num_rows) = &e.num_rows {
23200            self.write(", ");
23201            self.generate_expression(num_rows)?;
23202        }
23203        if let Some(ignore_nulls) = &e.ignore_feature_nulls {
23204            self.write(", ");
23205            self.generate_expression(ignore_nulls)?;
23206        }
23207        self.write(")");
23208        Ok(())
23209    }
23210
23211    fn generate_fetch(&mut self, e: &Fetch) -> Result<()> {
23212        // For dialects that prefer LIMIT, convert simple FETCH to LIMIT
23213        let use_limit = !e.percent && !e.with_ties && e.count.is_some() && matches!(
23214            self.config.dialect,
23215            Some(DialectType::Spark) | Some(DialectType::Hive)
23216            | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
23217            | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
23218            | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
23219        );
23220
23221        if use_limit {
23222            self.write_keyword("LIMIT");
23223            self.write_space();
23224            self.generate_expression(e.count.as_ref().unwrap())?;
23225            return Ok(());
23226        }
23227
23228        // Python: FETCH direction count limit_options
23229        self.write_keyword("FETCH");
23230        if !e.direction.is_empty() {
23231            self.write_space();
23232            self.write_keyword(&e.direction);
23233        }
23234        if let Some(count) = &e.count {
23235            self.write_space();
23236            self.generate_expression(count)?;
23237        }
23238        // Generate PERCENT, ROWS, WITH TIES/ONLY
23239        if e.percent {
23240            self.write_keyword(" PERCENT");
23241        }
23242        if e.rows {
23243            self.write_keyword(" ROWS");
23244        }
23245        if e.with_ties {
23246            self.write_keyword(" WITH TIES");
23247        } else if e.rows {
23248            self.write_keyword(" ONLY");
23249        } else {
23250            self.write_keyword(" ROWS ONLY");
23251        }
23252        Ok(())
23253    }
23254
23255    fn generate_file_format_property(&mut self, e: &FileFormatProperty) -> Result<()> {
23256        // For Hive format: STORED AS this or STORED AS INPUTFORMAT x OUTPUTFORMAT y
23257        // For Spark/Databricks without hive_format: USING this
23258        // For Snowflake/others: FILE_FORMAT = this or FILE_FORMAT = (expressions)
23259        if e.hive_format.is_some() {
23260            // Hive format: STORED AS ...
23261            self.write_keyword("STORED AS");
23262            self.write_space();
23263            if let Some(this) = &e.this {
23264                // Uppercase the format name (e.g., parquet -> PARQUET)
23265                if let Expression::Identifier(id) = this.as_ref() {
23266                    self.write_keyword(&id.name.to_uppercase());
23267                } else {
23268                    self.generate_expression(this)?;
23269                }
23270            }
23271        } else if matches!(self.config.dialect, Some(DialectType::Hive)) {
23272            // Hive: STORED AS format
23273            self.write_keyword("STORED AS");
23274            self.write_space();
23275            if let Some(this) = &e.this {
23276                if let Expression::Identifier(id) = this.as_ref() {
23277                    self.write_keyword(&id.name.to_uppercase());
23278                } else {
23279                    self.generate_expression(this)?;
23280                }
23281            }
23282        } else if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks)) {
23283            // Spark/Databricks: USING format (e.g., USING DELTA)
23284            self.write_keyword("USING");
23285            self.write_space();
23286            if let Some(this) = &e.this {
23287                self.generate_expression(this)?;
23288            }
23289        } else {
23290            // Snowflake/standard format
23291            self.write_keyword("FILE_FORMAT");
23292            self.write(" = ");
23293            if let Some(this) = &e.this {
23294                self.generate_expression(this)?;
23295            } else if !e.expressions.is_empty() {
23296                self.write("(");
23297                for (i, expr) in e.expressions.iter().enumerate() {
23298                    if i > 0 {
23299                        self.write(", ");
23300                    }
23301                    self.generate_expression(expr)?;
23302                }
23303                self.write(")");
23304            }
23305        }
23306        Ok(())
23307    }
23308
23309    fn generate_filter(&mut self, e: &Filter) -> Result<()> {
23310        // agg_func FILTER(WHERE condition)
23311        self.generate_expression(&e.this)?;
23312        self.write_space();
23313        self.write_keyword("FILTER");
23314        self.write("(");
23315        self.write_keyword("WHERE");
23316        self.write_space();
23317        self.generate_expression(&e.expression)?;
23318        self.write(")");
23319        Ok(())
23320    }
23321
23322    fn generate_float64(&mut self, e: &Float64) -> Result<()> {
23323        // FLOAT64(this) or FLOAT64(this, expression)
23324        self.write_keyword("FLOAT64");
23325        self.write("(");
23326        self.generate_expression(&e.this)?;
23327        if let Some(expr) = &e.expression {
23328            self.write(", ");
23329            self.generate_expression(expr)?;
23330        }
23331        self.write(")");
23332        Ok(())
23333    }
23334
23335    fn generate_for_in(&mut self, e: &ForIn) -> Result<()> {
23336        // FOR this DO expression
23337        self.write_keyword("FOR");
23338        self.write_space();
23339        self.generate_expression(&e.this)?;
23340        self.write_space();
23341        self.write_keyword("DO");
23342        self.write_space();
23343        self.generate_expression(&e.expression)?;
23344        Ok(())
23345    }
23346
23347    fn generate_foreign_key(&mut self, e: &ForeignKey) -> Result<()> {
23348        // FOREIGN KEY (cols) REFERENCES table(cols) ON DELETE action ON UPDATE action
23349        self.write_keyword("FOREIGN KEY");
23350        if !e.expressions.is_empty() {
23351            self.write(" (");
23352            for (i, expr) in e.expressions.iter().enumerate() {
23353                if i > 0 {
23354                    self.write(", ");
23355                }
23356                self.generate_expression(expr)?;
23357            }
23358            self.write(")");
23359        }
23360        if let Some(reference) = &e.reference {
23361            self.write_space();
23362            self.generate_expression(reference)?;
23363        }
23364        if let Some(delete) = &e.delete {
23365            self.write_space();
23366            self.write_keyword("ON DELETE");
23367            self.write_space();
23368            self.generate_expression(delete)?;
23369        }
23370        if let Some(update) = &e.update {
23371            self.write_space();
23372            self.write_keyword("ON UPDATE");
23373            self.write_space();
23374            self.generate_expression(update)?;
23375        }
23376        if !e.options.is_empty() {
23377            self.write_space();
23378            for (i, opt) in e.options.iter().enumerate() {
23379                if i > 0 {
23380                    self.write_space();
23381                }
23382                self.generate_expression(opt)?;
23383            }
23384        }
23385        Ok(())
23386    }
23387
23388    fn generate_format(&mut self, e: &Format) -> Result<()> {
23389        // FORMAT(this, expressions...)
23390        self.write_keyword("FORMAT");
23391        self.write("(");
23392        self.generate_expression(&e.this)?;
23393        for expr in &e.expressions {
23394            self.write(", ");
23395            self.generate_expression(expr)?;
23396        }
23397        self.write(")");
23398        Ok(())
23399    }
23400
23401    fn generate_format_phrase(&mut self, e: &FormatPhrase) -> Result<()> {
23402        // Teradata: column (FORMAT 'format_string')
23403        self.generate_expression(&e.this)?;
23404        self.write(" (");
23405        self.write_keyword("FORMAT");
23406        self.write(" '");
23407        self.write(&e.format);
23408        self.write("')");
23409        Ok(())
23410    }
23411
23412    fn generate_freespace_property(&mut self, e: &FreespaceProperty) -> Result<()> {
23413        // Python: FREESPACE=this[PERCENT]
23414        self.write_keyword("FREESPACE");
23415        self.write("=");
23416        self.generate_expression(&e.this)?;
23417        if e.percent.is_some() {
23418            self.write_keyword(" PERCENT");
23419        }
23420        Ok(())
23421    }
23422
23423    fn generate_from(&mut self, e: &From) -> Result<()> {
23424        // Python: return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
23425        self.write_keyword("FROM");
23426        self.write_space();
23427
23428        // BigQuery, Hive, Spark, Databricks, SQLite, and ClickHouse prefer explicit CROSS JOIN over comma syntax
23429        // But keep commas when TABLESAMPLE is present
23430        use crate::dialects::DialectType;
23431        let has_tablesample = e.expressions.iter().any(|expr| matches!(expr, Expression::TableSample(_)));
23432        let use_cross_join = !has_tablesample && matches!(
23433            self.config.dialect,
23434            Some(DialectType::BigQuery)
23435                | Some(DialectType::Hive)
23436                | Some(DialectType::Spark)
23437                | Some(DialectType::Databricks)
23438                | Some(DialectType::SQLite)
23439                | Some(DialectType::ClickHouse)
23440        );
23441
23442        // Snowflake wraps standalone VALUES in FROM clause with parentheses
23443        let wrap_values_in_parens = matches!(
23444            self.config.dialect,
23445            Some(DialectType::Snowflake)
23446        );
23447
23448        for (i, expr) in e.expressions.iter().enumerate() {
23449            if i > 0 {
23450                if use_cross_join {
23451                    self.write(" CROSS JOIN ");
23452                } else {
23453                    self.write(", ");
23454                }
23455            }
23456            if wrap_values_in_parens && matches!(expr, Expression::Values(_)) {
23457                self.write("(");
23458                self.generate_expression(expr)?;
23459                self.write(")");
23460            } else {
23461                self.generate_expression(expr)?;
23462            }
23463        }
23464        Ok(())
23465    }
23466
23467    fn generate_from_base(&mut self, e: &FromBase) -> Result<()> {
23468        // FROM_BASE(this, expression) - convert from base N
23469        self.write_keyword("FROM_BASE");
23470        self.write("(");
23471        self.generate_expression(&e.this)?;
23472        self.write(", ");
23473        self.generate_expression(&e.expression)?;
23474        self.write(")");
23475        Ok(())
23476    }
23477
23478    fn generate_from_time_zone(&mut self, e: &FromTimeZone) -> Result<()> {
23479        // this AT TIME ZONE zone AT TIME ZONE 'UTC'
23480        self.generate_expression(&e.this)?;
23481        if let Some(zone) = &e.zone {
23482            self.write_space();
23483            self.write_keyword("AT TIME ZONE");
23484            self.write_space();
23485            self.generate_expression(zone)?;
23486            self.write_space();
23487            self.write_keyword("AT TIME ZONE");
23488            self.write(" 'UTC'");
23489        }
23490        Ok(())
23491    }
23492
23493    fn generate_gap_fill(&mut self, e: &GapFill) -> Result<()> {
23494        // GAP_FILL(this, ts_column, bucket_width, ...)
23495        self.write_keyword("GAP_FILL");
23496        self.write("(");
23497        self.generate_expression(&e.this)?;
23498        if let Some(ts_column) = &e.ts_column {
23499            self.write(", ");
23500            self.generate_expression(ts_column)?;
23501        }
23502        if let Some(bucket_width) = &e.bucket_width {
23503            self.write(", ");
23504            self.generate_expression(bucket_width)?;
23505        }
23506        if let Some(partitioning_columns) = &e.partitioning_columns {
23507            self.write(", ");
23508            self.generate_expression(partitioning_columns)?;
23509        }
23510        if let Some(value_columns) = &e.value_columns {
23511            self.write(", ");
23512            self.generate_expression(value_columns)?;
23513        }
23514        self.write(")");
23515        Ok(())
23516    }
23517
23518    fn generate_generate_date_array(&mut self, e: &GenerateDateArray) -> Result<()> {
23519        // GENERATE_DATE_ARRAY(start, end, step)
23520        self.write_keyword("GENERATE_DATE_ARRAY");
23521        self.write("(");
23522        let mut first = true;
23523        if let Some(start) = &e.start {
23524            self.generate_expression(start)?;
23525            first = false;
23526        }
23527        if let Some(end) = &e.end {
23528            if !first {
23529                self.write(", ");
23530            }
23531            self.generate_expression(end)?;
23532            first = false;
23533        }
23534        if let Some(step) = &e.step {
23535            if !first {
23536                self.write(", ");
23537            }
23538            self.generate_expression(step)?;
23539        }
23540        self.write(")");
23541        Ok(())
23542    }
23543
23544    fn generate_generate_embedding(&mut self, e: &GenerateEmbedding) -> Result<()> {
23545        // ML.GENERATE_EMBEDDING(model, content, params)
23546        self.write_keyword("ML.GENERATE_EMBEDDING");
23547        self.write("(");
23548        self.generate_expression(&e.this)?;
23549        self.write(", ");
23550        self.generate_expression(&e.expression)?;
23551        if let Some(params) = &e.params_struct {
23552            self.write(", ");
23553            self.generate_expression(params)?;
23554        }
23555        self.write(")");
23556        Ok(())
23557    }
23558
23559    fn generate_generate_series(&mut self, e: &GenerateSeries) -> Result<()> {
23560        // GENERATE_SERIES(start, end, step)
23561        self.write_keyword("GENERATE_SERIES");
23562        self.write("(");
23563        let mut first = true;
23564        if let Some(start) = &e.start {
23565            self.generate_expression(start)?;
23566            first = false;
23567        }
23568        if let Some(end) = &e.end {
23569            if !first {
23570                self.write(", ");
23571            }
23572            self.generate_expression(end)?;
23573            first = false;
23574        }
23575        if let Some(step) = &e.step {
23576            if !first {
23577                self.write(", ");
23578            }
23579            self.generate_expression(step)?;
23580        }
23581        self.write(")");
23582        Ok(())
23583    }
23584
23585    fn generate_generate_timestamp_array(&mut self, e: &GenerateTimestampArray) -> Result<()> {
23586        // GENERATE_TIMESTAMP_ARRAY(start, end, step)
23587        self.write_keyword("GENERATE_TIMESTAMP_ARRAY");
23588        self.write("(");
23589        let mut first = true;
23590        if let Some(start) = &e.start {
23591            self.generate_expression(start)?;
23592            first = false;
23593        }
23594        if let Some(end) = &e.end {
23595            if !first {
23596                self.write(", ");
23597            }
23598            self.generate_expression(end)?;
23599            first = false;
23600        }
23601        if let Some(step) = &e.step {
23602            if !first {
23603                self.write(", ");
23604            }
23605            self.generate_expression(step)?;
23606        }
23607        self.write(")");
23608        Ok(())
23609    }
23610
23611    fn generate_generated_as_identity_column_constraint(&mut self, e: &GeneratedAsIdentityColumnConstraint) -> Result<()> {
23612        use crate::dialects::DialectType;
23613
23614        // For Snowflake, use AUTOINCREMENT START x INCREMENT y syntax
23615        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
23616            self.write_keyword("AUTOINCREMENT");
23617            if let Some(start) = &e.start {
23618                self.write_keyword(" START ");
23619                self.generate_expression(start)?;
23620            }
23621            if let Some(increment) = &e.increment {
23622                self.write_keyword(" INCREMENT ");
23623                self.generate_expression(increment)?;
23624            }
23625            return Ok(());
23626        }
23627
23628        // Python: GENERATED [ALWAYS|BY DEFAULT [ON NULL]] AS IDENTITY [(start, increment, ...)]
23629        self.write_keyword("GENERATED");
23630        if let Some(this) = &e.this {
23631            // Check if it's a truthy boolean expression
23632            if let Expression::Boolean(b) = this.as_ref() {
23633                if b.value {
23634                    self.write_keyword(" ALWAYS");
23635                } else {
23636                    self.write_keyword(" BY DEFAULT");
23637                    if e.on_null.is_some() {
23638                        self.write_keyword(" ON NULL");
23639                    }
23640                }
23641            } else {
23642                self.write_keyword(" ALWAYS");
23643            }
23644        }
23645        self.write_keyword(" AS IDENTITY");
23646        // Add sequence options if any
23647        let has_options = e.start.is_some() || e.increment.is_some() || e.minvalue.is_some() || e.maxvalue.is_some();
23648        if has_options {
23649            self.write(" (");
23650            let mut first = true;
23651            if let Some(start) = &e.start {
23652                self.write_keyword("START WITH ");
23653                self.generate_expression(start)?;
23654                first = false;
23655            }
23656            if let Some(increment) = &e.increment {
23657                if !first { self.write(" "); }
23658                self.write_keyword("INCREMENT BY ");
23659                self.generate_expression(increment)?;
23660                first = false;
23661            }
23662            if let Some(minvalue) = &e.minvalue {
23663                if !first { self.write(" "); }
23664                self.write_keyword("MINVALUE ");
23665                self.generate_expression(minvalue)?;
23666                first = false;
23667            }
23668            if let Some(maxvalue) = &e.maxvalue {
23669                if !first { self.write(" "); }
23670                self.write_keyword("MAXVALUE ");
23671                self.generate_expression(maxvalue)?;
23672            }
23673            self.write(")");
23674        }
23675        Ok(())
23676    }
23677
23678    fn generate_generated_as_row_column_constraint(&mut self, e: &GeneratedAsRowColumnConstraint) -> Result<()> {
23679        // Python: GENERATED ALWAYS AS ROW START|END [HIDDEN]
23680        self.write_keyword("GENERATED ALWAYS AS ROW ");
23681        if e.start.is_some() {
23682            self.write_keyword("START");
23683        } else {
23684            self.write_keyword("END");
23685        }
23686        if e.hidden.is_some() {
23687            self.write_keyword(" HIDDEN");
23688        }
23689        Ok(())
23690    }
23691
23692    fn generate_get(&mut self, e: &Get) -> Result<()> {
23693        // GET this target properties
23694        self.write_keyword("GET");
23695        self.write_space();
23696        self.generate_expression(&e.this)?;
23697        if let Some(target) = &e.target {
23698            self.write_space();
23699            self.generate_expression(target)?;
23700        }
23701        for prop in &e.properties {
23702            self.write_space();
23703            self.generate_expression(prop)?;
23704        }
23705        Ok(())
23706    }
23707
23708    fn generate_get_extract(&mut self, e: &GetExtract) -> Result<()> {
23709        // GetExtract generates bracket access: this[expression]
23710        self.generate_expression(&e.this)?;
23711        self.write("[");
23712        self.generate_expression(&e.expression)?;
23713        self.write("]");
23714        Ok(())
23715    }
23716
23717    fn generate_getbit(&mut self, e: &Getbit) -> Result<()> {
23718        // GETBIT(this, expression) or GET_BIT(this, expression)
23719        self.write_keyword("GETBIT");
23720        self.write("(");
23721        self.generate_expression(&e.this)?;
23722        self.write(", ");
23723        self.generate_expression(&e.expression)?;
23724        self.write(")");
23725        Ok(())
23726    }
23727
23728    fn generate_grant_principal(&mut self, e: &GrantPrincipal) -> Result<()> {
23729        // [ROLE|GROUP] name (e.g., "ROLE admin", "GROUP qa_users", or just "user1")
23730        if e.is_role {
23731            self.write_keyword("ROLE");
23732            self.write_space();
23733        } else if e.is_group {
23734            self.write_keyword("GROUP");
23735            self.write_space();
23736        }
23737        self.write(&e.name.name);
23738        Ok(())
23739    }
23740
23741    fn generate_grant_privilege(&mut self, e: &GrantPrivilege) -> Result<()> {
23742        // privilege(columns) or just privilege
23743        self.generate_expression(&e.this)?;
23744        if !e.expressions.is_empty() {
23745            self.write("(");
23746            for (i, expr) in e.expressions.iter().enumerate() {
23747                if i > 0 {
23748                    self.write(", ");
23749                }
23750                self.generate_expression(expr)?;
23751            }
23752            self.write(")");
23753        }
23754        Ok(())
23755    }
23756
23757    fn generate_group(&mut self, e: &Group) -> Result<()> {
23758        // Python handles GROUP BY ALL/DISTINCT modifiers and grouping expressions
23759        self.write_keyword("GROUP BY");
23760        // Handle ALL/DISTINCT modifier: Some(true) = ALL, Some(false) = DISTINCT
23761        match e.all {
23762            Some(true) => {
23763                self.write_space();
23764                self.write_keyword("ALL");
23765            }
23766            Some(false) => {
23767                self.write_space();
23768                self.write_keyword("DISTINCT");
23769            }
23770            None => {}
23771        }
23772        if !e.expressions.is_empty() {
23773            self.write_space();
23774            for (i, expr) in e.expressions.iter().enumerate() {
23775                if i > 0 {
23776                    self.write(", ");
23777                }
23778                self.generate_expression(expr)?;
23779            }
23780        }
23781        // Handle CUBE, ROLLUP, GROUPING SETS
23782        if let Some(cube) = &e.cube {
23783            if !e.expressions.is_empty() {
23784                self.write(", ");
23785            } else {
23786                self.write_space();
23787            }
23788            self.generate_expression(cube)?;
23789        }
23790        if let Some(rollup) = &e.rollup {
23791            if !e.expressions.is_empty() || e.cube.is_some() {
23792                self.write(", ");
23793            } else {
23794                self.write_space();
23795            }
23796            self.generate_expression(rollup)?;
23797        }
23798        if let Some(grouping_sets) = &e.grouping_sets {
23799            if !e.expressions.is_empty() || e.cube.is_some() || e.rollup.is_some() {
23800                self.write(", ");
23801            } else {
23802                self.write_space();
23803            }
23804            self.generate_expression(grouping_sets)?;
23805        }
23806        if let Some(totals) = &e.totals {
23807            self.write_space();
23808            self.write_keyword("WITH TOTALS");
23809            self.generate_expression(totals)?;
23810        }
23811        Ok(())
23812    }
23813
23814    fn generate_group_by(&mut self, e: &GroupBy) -> Result<()> {
23815        // GROUP BY expressions
23816        self.write_keyword("GROUP BY");
23817        // Handle ALL/DISTINCT modifier: Some(true) = ALL, Some(false) = DISTINCT
23818        match e.all {
23819            Some(true) => {
23820                self.write_space();
23821                self.write_keyword("ALL");
23822            }
23823            Some(false) => {
23824                self.write_space();
23825                self.write_keyword("DISTINCT");
23826            }
23827            None => {}
23828        }
23829
23830        // Check for trailing WITH CUBE or WITH ROLLUP (Hive/MySQL syntax)
23831        // These are represented as Cube/Rollup expressions with empty expressions at the end
23832        let mut trailing_cube = false;
23833        let mut trailing_rollup = false;
23834        let mut regular_expressions: Vec<&Expression> = Vec::new();
23835
23836        for expr in &e.expressions {
23837            match expr {
23838                Expression::Cube(c) if c.expressions.is_empty() => {
23839                    trailing_cube = true;
23840                }
23841                Expression::Rollup(r) if r.expressions.is_empty() => {
23842                    trailing_rollup = true;
23843                }
23844                _ => {
23845                    regular_expressions.push(expr);
23846                }
23847            }
23848        }
23849
23850        // In pretty mode, put columns on separate lines
23851        if self.config.pretty {
23852            self.write_newline();
23853            self.indent_level += 1;
23854            for (i, expr) in regular_expressions.iter().enumerate() {
23855                if i > 0 {
23856                    self.write(",");
23857                    self.write_newline();
23858                }
23859                self.write_indent();
23860                self.generate_expression(expr)?;
23861            }
23862            self.indent_level -= 1;
23863        } else {
23864            self.write_space();
23865            for (i, expr) in regular_expressions.iter().enumerate() {
23866                if i > 0 {
23867                    self.write(", ");
23868                }
23869                self.generate_expression(expr)?;
23870            }
23871        }
23872
23873        // Output trailing WITH CUBE or WITH ROLLUP
23874        if trailing_cube {
23875            self.write_space();
23876            self.write_keyword("WITH CUBE");
23877        } else if trailing_rollup {
23878            self.write_space();
23879            self.write_keyword("WITH ROLLUP");
23880        }
23881
23882        // ClickHouse: WITH TOTALS
23883        if e.totals {
23884            self.write_space();
23885            self.write_keyword("WITH TOTALS");
23886        }
23887
23888        Ok(())
23889    }
23890
23891    fn generate_grouping(&mut self, e: &Grouping) -> Result<()> {
23892        // GROUPING(col1, col2, ...)
23893        self.write_keyword("GROUPING");
23894        self.write("(");
23895        for (i, expr) in e.expressions.iter().enumerate() {
23896            if i > 0 {
23897                self.write(", ");
23898            }
23899            self.generate_expression(expr)?;
23900        }
23901        self.write(")");
23902        Ok(())
23903    }
23904
23905    fn generate_grouping_id(&mut self, e: &GroupingId) -> Result<()> {
23906        // GROUPING_ID(col1, col2, ...)
23907        self.write_keyword("GROUPING_ID");
23908        self.write("(");
23909        for (i, expr) in e.expressions.iter().enumerate() {
23910            if i > 0 {
23911                self.write(", ");
23912            }
23913            self.generate_expression(expr)?;
23914        }
23915        self.write(")");
23916        Ok(())
23917    }
23918
23919    fn generate_grouping_sets(&mut self, e: &GroupingSets) -> Result<()> {
23920        // Python: return f"GROUPING SETS {self.wrap(grouping_sets)}"
23921        self.write_keyword("GROUPING SETS");
23922        self.write(" (");
23923        for (i, expr) in e.expressions.iter().enumerate() {
23924            if i > 0 {
23925                self.write(", ");
23926            }
23927            self.generate_expression(expr)?;
23928        }
23929        self.write(")");
23930        Ok(())
23931    }
23932
23933    fn generate_hash_agg(&mut self, e: &HashAgg) -> Result<()> {
23934        // HASH_AGG(this, expressions...)
23935        self.write_keyword("HASH_AGG");
23936        self.write("(");
23937        self.generate_expression(&e.this)?;
23938        for expr in &e.expressions {
23939            self.write(", ");
23940            self.generate_expression(expr)?;
23941        }
23942        self.write(")");
23943        Ok(())
23944    }
23945
23946    fn generate_having(&mut self, e: &Having) -> Result<()> {
23947        // Python: return f"{self.seg('HAVING')}{self.sep()}{this}"
23948        self.write_keyword("HAVING");
23949        self.write_space();
23950        self.generate_expression(&e.this)?;
23951        Ok(())
23952    }
23953
23954    fn generate_having_max(&mut self, e: &HavingMax) -> Result<()> {
23955        // Python: this HAVING MAX|MIN expression
23956        self.generate_expression(&e.this)?;
23957        self.write_space();
23958        self.write_keyword("HAVING");
23959        self.write_space();
23960        if e.max.is_some() {
23961            self.write_keyword("MAX");
23962        } else {
23963            self.write_keyword("MIN");
23964        }
23965        self.write_space();
23966        self.generate_expression(&e.expression)?;
23967        Ok(())
23968    }
23969
23970    fn generate_heredoc(&mut self, e: &Heredoc) -> Result<()> {
23971        use crate::dialects::DialectType;
23972        // DuckDB: convert dollar-tagged strings to single-quoted
23973        if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
23974            // Extract the string content and output as single-quoted
23975            if let Expression::Literal(Literal::String(ref s)) = *e.this {
23976                return self.generate_string_literal(s);
23977            }
23978        }
23979        // PostgreSQL: preserve dollar-quoting
23980        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
23981            self.write("$");
23982            if let Some(tag) = &e.tag {
23983                self.generate_expression(tag)?;
23984            }
23985            self.write("$");
23986            self.generate_expression(&e.this)?;
23987            self.write("$");
23988            if let Some(tag) = &e.tag {
23989                self.generate_expression(tag)?;
23990            }
23991            self.write("$");
23992            return Ok(());
23993        }
23994        // Default: output as dollar-tagged
23995        self.write("$");
23996        if let Some(tag) = &e.tag {
23997            self.generate_expression(tag)?;
23998        }
23999        self.write("$");
24000        self.generate_expression(&e.this)?;
24001        self.write("$");
24002        if let Some(tag) = &e.tag {
24003            self.generate_expression(tag)?;
24004        }
24005        self.write("$");
24006        Ok(())
24007    }
24008
24009    fn generate_hex_encode(&mut self, e: &HexEncode) -> Result<()> {
24010        // HEX_ENCODE(this)
24011        self.write_keyword("HEX_ENCODE");
24012        self.write("(");
24013        self.generate_expression(&e.this)?;
24014        self.write(")");
24015        Ok(())
24016    }
24017
24018    fn generate_historical_data(&mut self, e: &HistoricalData) -> Result<()> {
24019        // Python: this (kind => expression)
24020        // Write the keyword (AT/BEFORE/END) directly to avoid quoting it as a reserved word
24021        match e.this.as_ref() {
24022            Expression::Identifier(id) => self.write(&id.name),
24023            other => self.generate_expression(other)?,
24024        }
24025        self.write(" (");
24026        self.write(&e.kind);
24027        self.write(" => ");
24028        self.generate_expression(&e.expression)?;
24029        self.write(")");
24030        Ok(())
24031    }
24032
24033    fn generate_hll(&mut self, e: &Hll) -> Result<()> {
24034        // HLL(this, expressions...)
24035        self.write_keyword("HLL");
24036        self.write("(");
24037        self.generate_expression(&e.this)?;
24038        for expr in &e.expressions {
24039            self.write(", ");
24040            self.generate_expression(expr)?;
24041        }
24042        self.write(")");
24043        Ok(())
24044    }
24045
24046    fn generate_in_out_column_constraint(&mut self, e: &InOutColumnConstraint) -> Result<()> {
24047        // Python: IN|OUT|IN OUT
24048        if e.input_.is_some() && e.output.is_some() {
24049            self.write_keyword("IN OUT");
24050        } else if e.input_.is_some() {
24051            self.write_keyword("IN");
24052        } else if e.output.is_some() {
24053            self.write_keyword("OUT");
24054        }
24055        Ok(())
24056    }
24057
24058    fn generate_include_property(&mut self, e: &IncludeProperty) -> Result<()> {
24059        // Python: INCLUDE this [column_def] [AS alias]
24060        self.write_keyword("INCLUDE");
24061        self.write_space();
24062        self.generate_expression(&e.this)?;
24063        if let Some(column_def) = &e.column_def {
24064            self.write_space();
24065            self.generate_expression(column_def)?;
24066        }
24067        if let Some(alias) = &e.alias {
24068            self.write_space();
24069            self.write_keyword("AS");
24070            self.write_space();
24071            self.write(alias);
24072        }
24073        Ok(())
24074    }
24075
24076    fn generate_index(&mut self, e: &Index) -> Result<()> {
24077        // [UNIQUE] [PRIMARY] [AMP] INDEX [name] [ON table] (params)
24078        if e.unique {
24079            self.write_keyword("UNIQUE");
24080            self.write_space();
24081        }
24082        if e.primary.is_some() {
24083            self.write_keyword("PRIMARY");
24084            self.write_space();
24085        }
24086        if e.amp.is_some() {
24087            self.write_keyword("AMP");
24088            self.write_space();
24089        }
24090        if e.table.is_none() {
24091            self.write_keyword("INDEX");
24092            self.write_space();
24093        }
24094        if let Some(name) = &e.this {
24095            self.generate_expression(name)?;
24096            self.write_space();
24097        }
24098        if let Some(table) = &e.table {
24099            self.write_keyword("ON");
24100            self.write_space();
24101            self.generate_expression(table)?;
24102        }
24103        if !e.params.is_empty() {
24104            self.write("(");
24105            for (i, param) in e.params.iter().enumerate() {
24106                if i > 0 {
24107                    self.write(", ");
24108                }
24109                self.generate_expression(param)?;
24110            }
24111            self.write(")");
24112        }
24113        Ok(())
24114    }
24115
24116    fn generate_index_column_constraint(&mut self, e: &IndexColumnConstraint) -> Result<()> {
24117        // Python: kind INDEX [this] [USING index_type] (expressions) [options]
24118        if let Some(kind) = &e.kind {
24119            self.write(kind);
24120            self.write_space();
24121        }
24122        self.write_keyword("INDEX");
24123        if let Some(this) = &e.this {
24124            self.write_space();
24125            self.generate_expression(this)?;
24126        }
24127        if let Some(index_type) = &e.index_type {
24128            self.write_space();
24129            self.write_keyword("USING");
24130            self.write_space();
24131            self.generate_expression(index_type)?;
24132        }
24133        if !e.expressions.is_empty() {
24134            self.write(" (");
24135            for (i, expr) in e.expressions.iter().enumerate() {
24136                if i > 0 {
24137                    self.write(", ");
24138                }
24139                self.generate_expression(expr)?;
24140            }
24141            self.write(")");
24142        }
24143        for opt in &e.options {
24144            self.write_space();
24145            self.generate_expression(opt)?;
24146        }
24147        Ok(())
24148    }
24149
24150    fn generate_index_constraint_option(&mut self, e: &IndexConstraintOption) -> Result<()> {
24151        // Python: KEY_BLOCK_SIZE = x | USING x | WITH PARSER x | COMMENT x | visible | engine_attr | secondary_engine_attr
24152        if let Some(key_block_size) = &e.key_block_size {
24153            self.write_keyword("KEY_BLOCK_SIZE");
24154            self.write(" = ");
24155            self.generate_expression(key_block_size)?;
24156        } else if let Some(using) = &e.using {
24157            self.write_keyword("USING");
24158            self.write_space();
24159            self.generate_expression(using)?;
24160        } else if let Some(parser) = &e.parser {
24161            self.write_keyword("WITH PARSER");
24162            self.write_space();
24163            self.generate_expression(parser)?;
24164        } else if let Some(comment) = &e.comment {
24165            self.write_keyword("COMMENT");
24166            self.write_space();
24167            self.generate_expression(comment)?;
24168        } else if let Some(visible) = &e.visible {
24169            self.generate_expression(visible)?;
24170        } else if let Some(engine_attr) = &e.engine_attr {
24171            self.write_keyword("ENGINE_ATTRIBUTE");
24172            self.write(" = ");
24173            self.generate_expression(engine_attr)?;
24174        } else if let Some(secondary_engine_attr) = &e.secondary_engine_attr {
24175            self.write_keyword("SECONDARY_ENGINE_ATTRIBUTE");
24176            self.write(" = ");
24177            self.generate_expression(secondary_engine_attr)?;
24178        }
24179        Ok(())
24180    }
24181
24182    fn generate_index_parameters(&mut self, e: &IndexParameters) -> Result<()> {
24183        // Python: [USING using] (columns) [PARTITION BY partition_by] [where] [INCLUDE (include)] [WITH (with_storage)] [USING INDEX TABLESPACE tablespace]
24184        if let Some(using) = &e.using {
24185            self.write_keyword("USING");
24186            self.write_space();
24187            self.generate_expression(using)?;
24188        }
24189        if !e.columns.is_empty() {
24190            self.write("(");
24191            for (i, col) in e.columns.iter().enumerate() {
24192                if i > 0 {
24193                    self.write(", ");
24194                }
24195                self.generate_expression(col)?;
24196            }
24197            self.write(")");
24198        }
24199        if let Some(partition_by) = &e.partition_by {
24200            self.write_space();
24201            self.write_keyword("PARTITION BY");
24202            self.write_space();
24203            self.generate_expression(partition_by)?;
24204        }
24205        if let Some(where_) = &e.where_ {
24206            self.write_space();
24207            self.generate_expression(where_)?;
24208        }
24209        if let Some(include) = &e.include {
24210            self.write_space();
24211            self.write_keyword("INCLUDE");
24212            self.write(" (");
24213            self.generate_expression(include)?;
24214            self.write(")");
24215        }
24216        if let Some(with_storage) = &e.with_storage {
24217            self.write_space();
24218            self.write_keyword("WITH");
24219            self.write(" (");
24220            self.generate_expression(with_storage)?;
24221            self.write(")");
24222        }
24223        if let Some(tablespace) = &e.tablespace {
24224            self.write_space();
24225            self.write_keyword("USING INDEX TABLESPACE");
24226            self.write_space();
24227            self.generate_expression(tablespace)?;
24228        }
24229        Ok(())
24230    }
24231
24232    fn generate_index_table_hint(&mut self, e: &IndexTableHint) -> Result<()> {
24233        // Python: this INDEX [FOR target] (expressions)
24234        // Write hint type (USE/IGNORE/FORCE) as keyword, not through generate_expression
24235        // to avoid quoting reserved keywords like IGNORE, FORCE, JOIN
24236        if let Expression::Identifier(id) = &*e.this {
24237            self.write_keyword(&id.name);
24238        } else {
24239            self.generate_expression(&e.this)?;
24240        }
24241        self.write_space();
24242        self.write_keyword("INDEX");
24243        if let Some(target) = &e.target {
24244            self.write_space();
24245            self.write_keyword("FOR");
24246            self.write_space();
24247            if let Expression::Identifier(id) = &**target {
24248                self.write_keyword(&id.name);
24249            } else {
24250                self.generate_expression(target)?;
24251            }
24252        }
24253        // Always output parentheses (even if empty, e.g. USE INDEX ())
24254        self.write(" (");
24255        for (i, expr) in e.expressions.iter().enumerate() {
24256            if i > 0 {
24257                self.write(", ");
24258            }
24259            self.generate_expression(expr)?;
24260        }
24261        self.write(")");
24262        Ok(())
24263    }
24264
24265    fn generate_inherits_property(&mut self, e: &InheritsProperty) -> Result<()> {
24266        // INHERITS (table1, table2, ...)
24267        self.write_keyword("INHERITS");
24268        self.write(" (");
24269        for (i, expr) in e.expressions.iter().enumerate() {
24270            if i > 0 {
24271                self.write(", ");
24272            }
24273            self.generate_expression(expr)?;
24274        }
24275        self.write(")");
24276        Ok(())
24277    }
24278
24279    fn generate_input_model_property(&mut self, e: &InputModelProperty) -> Result<()> {
24280        // INPUT(model)
24281        self.write_keyword("INPUT");
24282        self.write("(");
24283        self.generate_expression(&e.this)?;
24284        self.write(")");
24285        Ok(())
24286    }
24287
24288    fn generate_input_output_format(&mut self, e: &InputOutputFormat) -> Result<()> {
24289        // Python: INPUTFORMAT input_format OUTPUTFORMAT output_format
24290        if let Some(input_format) = &e.input_format {
24291            self.write_keyword("INPUTFORMAT");
24292            self.write_space();
24293            self.generate_expression(input_format)?;
24294        }
24295        if let Some(output_format) = &e.output_format {
24296            if e.input_format.is_some() {
24297                self.write(" ");
24298            }
24299            self.write_keyword("OUTPUTFORMAT");
24300            self.write_space();
24301            self.generate_expression(output_format)?;
24302        }
24303        Ok(())
24304    }
24305
24306    fn generate_install(&mut self, e: &Install) -> Result<()> {
24307        // [FORCE] INSTALL extension [FROM source]
24308        if e.force.is_some() {
24309            self.write_keyword("FORCE");
24310            self.write_space();
24311        }
24312        self.write_keyword("INSTALL");
24313        self.write_space();
24314        self.generate_expression(&e.this)?;
24315        if let Some(from) = &e.from_ {
24316            self.write_space();
24317            self.write_keyword("FROM");
24318            self.write_space();
24319            self.generate_expression(from)?;
24320        }
24321        Ok(())
24322    }
24323
24324    fn generate_interval_op(&mut self, e: &IntervalOp) -> Result<()> {
24325        // INTERVAL 'expression' unit
24326        self.write_keyword("INTERVAL");
24327        self.write_space();
24328        // When a unit is specified and the expression is a number,
24329        self.generate_expression(&e.expression)?;
24330        if let Some(unit) = &e.unit {
24331            self.write_space();
24332            self.write(unit);
24333        }
24334        Ok(())
24335    }
24336
24337    fn generate_interval_span(&mut self, e: &IntervalSpan) -> Result<()> {
24338        // unit TO unit (e.g., HOUR TO SECOND)
24339        self.write(&format!("{:?}", e.this).to_uppercase());
24340        self.write_space();
24341        self.write_keyword("TO");
24342        self.write_space();
24343        self.write(&format!("{:?}", e.expression).to_uppercase());
24344        Ok(())
24345    }
24346
24347    fn generate_into_clause(&mut self, e: &IntoClause) -> Result<()> {
24348        // INTO [TEMPORARY|UNLOGGED] table
24349        self.write_keyword("INTO");
24350        if e.temporary {
24351            self.write_keyword(" TEMPORARY");
24352        }
24353        if e.unlogged.is_some() {
24354            self.write_keyword(" UNLOGGED");
24355        }
24356        if let Some(this) = &e.this {
24357            self.write_space();
24358            self.generate_expression(this)?;
24359        }
24360        if !e.expressions.is_empty() {
24361            self.write(" (");
24362            for (i, expr) in e.expressions.iter().enumerate() {
24363                if i > 0 {
24364                    self.write(", ");
24365                }
24366                self.generate_expression(expr)?;
24367            }
24368            self.write(")");
24369        }
24370        Ok(())
24371    }
24372
24373    fn generate_introducer(&mut self, e: &Introducer) -> Result<()> {
24374        // Python: this expression (e.g., _utf8 'string')
24375        self.generate_expression(&e.this)?;
24376        self.write_space();
24377        self.generate_expression(&e.expression)?;
24378        Ok(())
24379    }
24380
24381    fn generate_isolated_loading_property(&mut self, e: &IsolatedLoadingProperty) -> Result<()> {
24382        // Python: WITH [NO] [CONCURRENT] ISOLATED LOADING [target]
24383        self.write_keyword("WITH");
24384        if e.no.is_some() {
24385            self.write_keyword(" NO");
24386        }
24387        if e.concurrent.is_some() {
24388            self.write_keyword(" CONCURRENT");
24389        }
24390        self.write_keyword(" ISOLATED LOADING");
24391        if let Some(target) = &e.target {
24392            self.write_space();
24393            self.generate_expression(target)?;
24394        }
24395        Ok(())
24396    }
24397
24398    fn generate_json(&mut self, e: &JSON) -> Result<()> {
24399        // Python: JSON [this] [WITHOUT|WITH] [UNIQUE KEYS]
24400        self.write_keyword("JSON");
24401        if let Some(this) = &e.this {
24402            self.write_space();
24403            self.generate_expression(this)?;
24404        }
24405        if let Some(with_) = &e.with_ {
24406            // Check if it's a truthy boolean
24407            if let Expression::Boolean(b) = with_.as_ref() {
24408                if b.value {
24409                    self.write_keyword(" WITH");
24410                } else {
24411                    self.write_keyword(" WITHOUT");
24412                }
24413            }
24414        }
24415        if e.unique {
24416            self.write_keyword(" UNIQUE KEYS");
24417        }
24418        Ok(())
24419    }
24420
24421    fn generate_json_array(&mut self, e: &JSONArray) -> Result<()> {
24422        // Python: return self.func("JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})")
24423        self.write_keyword("JSON_ARRAY");
24424        self.write("(");
24425        for (i, expr) in e.expressions.iter().enumerate() {
24426            if i > 0 {
24427                self.write(", ");
24428            }
24429            self.generate_expression(expr)?;
24430        }
24431        if let Some(null_handling) = &e.null_handling {
24432            self.write_space();
24433            self.generate_expression(null_handling)?;
24434        }
24435        if let Some(return_type) = &e.return_type {
24436            self.write_space();
24437            self.write_keyword("RETURNING");
24438            self.write_space();
24439            self.generate_expression(return_type)?;
24440        }
24441        if e.strict.is_some() {
24442            self.write_space();
24443            self.write_keyword("STRICT");
24444        }
24445        self.write(")");
24446        Ok(())
24447    }
24448
24449    fn generate_json_array_agg_struct(&mut self, e: &JSONArrayAgg) -> Result<()> {
24450        // JSON_ARRAYAGG(this [ORDER BY ...] [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
24451        self.write_keyword("JSON_ARRAYAGG");
24452        self.write("(");
24453        self.generate_expression(&e.this)?;
24454        if let Some(order) = &e.order {
24455            self.write_space();
24456            // Order is stored as an OrderBy expression
24457            if let Expression::OrderBy(ob) = order.as_ref() {
24458                self.write_keyword("ORDER BY");
24459                self.write_space();
24460                for (i, ord) in ob.expressions.iter().enumerate() {
24461                    if i > 0 {
24462                        self.write(", ");
24463                    }
24464                    self.generate_ordered(ord)?;
24465                }
24466            } else {
24467                // Fallback: generate the expression directly
24468                self.generate_expression(order)?;
24469            }
24470        }
24471        if let Some(null_handling) = &e.null_handling {
24472            self.write_space();
24473            self.generate_expression(null_handling)?;
24474        }
24475        if let Some(return_type) = &e.return_type {
24476            self.write_space();
24477            self.write_keyword("RETURNING");
24478            self.write_space();
24479            self.generate_expression(return_type)?;
24480        }
24481        if e.strict.is_some() {
24482            self.write_space();
24483            self.write_keyword("STRICT");
24484        }
24485        self.write(")");
24486        Ok(())
24487    }
24488
24489    fn generate_json_object_agg_struct(&mut self, e: &JSONObjectAgg) -> Result<()> {
24490        // JSON_OBJECTAGG(key: value [NULL ON NULL | ABSENT ON NULL] [WITH UNIQUE KEYS] [RETURNING type])
24491        self.write_keyword("JSON_OBJECTAGG");
24492        self.write("(");
24493        for (i, expr) in e.expressions.iter().enumerate() {
24494            if i > 0 {
24495                self.write(", ");
24496            }
24497            self.generate_expression(expr)?;
24498        }
24499        if let Some(null_handling) = &e.null_handling {
24500            self.write_space();
24501            self.generate_expression(null_handling)?;
24502        }
24503        if let Some(unique_keys) = &e.unique_keys {
24504            self.write_space();
24505            if let Expression::Boolean(b) = unique_keys.as_ref() {
24506                if b.value {
24507                    self.write_keyword("WITH UNIQUE KEYS");
24508                } else {
24509                    self.write_keyword("WITHOUT UNIQUE KEYS");
24510                }
24511            }
24512        }
24513        if let Some(return_type) = &e.return_type {
24514            self.write_space();
24515            self.write_keyword("RETURNING");
24516            self.write_space();
24517            self.generate_expression(return_type)?;
24518        }
24519        self.write(")");
24520        Ok(())
24521    }
24522
24523    fn generate_json_array_append(&mut self, e: &JSONArrayAppend) -> Result<()> {
24524        // JSON_ARRAY_APPEND(this, path, value, ...)
24525        self.write_keyword("JSON_ARRAY_APPEND");
24526        self.write("(");
24527        self.generate_expression(&e.this)?;
24528        for expr in &e.expressions {
24529            self.write(", ");
24530            self.generate_expression(expr)?;
24531        }
24532        self.write(")");
24533        Ok(())
24534    }
24535
24536    fn generate_json_array_contains(&mut self, e: &JSONArrayContains) -> Result<()> {
24537        // JSON_ARRAY_CONTAINS(this, expression)
24538        self.write_keyword("JSON_ARRAY_CONTAINS");
24539        self.write("(");
24540        self.generate_expression(&e.this)?;
24541        self.write(", ");
24542        self.generate_expression(&e.expression)?;
24543        self.write(")");
24544        Ok(())
24545    }
24546
24547    fn generate_json_array_insert(&mut self, e: &JSONArrayInsert) -> Result<()> {
24548        // JSON_ARRAY_INSERT(this, path, value, ...)
24549        self.write_keyword("JSON_ARRAY_INSERT");
24550        self.write("(");
24551        self.generate_expression(&e.this)?;
24552        for expr in &e.expressions {
24553            self.write(", ");
24554            self.generate_expression(expr)?;
24555        }
24556        self.write(")");
24557        Ok(())
24558    }
24559
24560    fn generate_jsonb_exists(&mut self, e: &JSONBExists) -> Result<()> {
24561        // JSONB_EXISTS(this, path)
24562        self.write_keyword("JSONB_EXISTS");
24563        self.write("(");
24564        self.generate_expression(&e.this)?;
24565        if let Some(path) = &e.path {
24566            self.write(", ");
24567            self.generate_expression(path)?;
24568        }
24569        self.write(")");
24570        Ok(())
24571    }
24572
24573    fn generate_jsonb_extract_scalar(&mut self, e: &JSONBExtractScalar) -> Result<()> {
24574        // JSONB_EXTRACT_SCALAR(this, expression)
24575        self.write_keyword("JSONB_EXTRACT_SCALAR");
24576        self.write("(");
24577        self.generate_expression(&e.this)?;
24578        self.write(", ");
24579        self.generate_expression(&e.expression)?;
24580        self.write(")");
24581        Ok(())
24582    }
24583
24584    fn generate_jsonb_object_agg(&mut self, e: &JSONBObjectAgg) -> Result<()> {
24585        // JSONB_OBJECT_AGG(this, expression)
24586        self.write_keyword("JSONB_OBJECT_AGG");
24587        self.write("(");
24588        self.generate_expression(&e.this)?;
24589        self.write(", ");
24590        self.generate_expression(&e.expression)?;
24591        self.write(")");
24592        Ok(())
24593    }
24594
24595    fn generate_json_column_def(&mut self, e: &JSONColumnDef) -> Result<()> {
24596        // Python: NESTED PATH path schema | this kind PATH path [FOR ORDINALITY]
24597        if let Some(nested_schema) = &e.nested_schema {
24598            self.write_keyword("NESTED");
24599            if let Some(path) = &e.path {
24600                self.write_space();
24601                self.write_keyword("PATH");
24602                self.write_space();
24603                self.generate_expression(path)?;
24604            }
24605            self.write_space();
24606            self.generate_expression(nested_schema)?;
24607        } else {
24608            if let Some(this) = &e.this {
24609                self.generate_expression(this)?;
24610            }
24611            if let Some(kind) = &e.kind {
24612                self.write_space();
24613                self.write(kind);
24614            }
24615            if let Some(path) = &e.path {
24616                self.write_space();
24617                self.write_keyword("PATH");
24618                self.write_space();
24619                self.generate_expression(path)?;
24620            }
24621            if e.ordinality.is_some() {
24622                self.write_keyword(" FOR ORDINALITY");
24623            }
24624        }
24625        Ok(())
24626    }
24627
24628    fn generate_json_exists(&mut self, e: &JSONExists) -> Result<()> {
24629        // JSON_EXISTS(this, path PASSING vars ON ERROR/EMPTY condition)
24630        self.write_keyword("JSON_EXISTS");
24631        self.write("(");
24632        self.generate_expression(&e.this)?;
24633        if let Some(path) = &e.path {
24634            self.write(", ");
24635            self.generate_expression(path)?;
24636        }
24637        if let Some(passing) = &e.passing {
24638            self.write_space();
24639            self.write_keyword("PASSING");
24640            self.write_space();
24641            self.generate_expression(passing)?;
24642        }
24643        if let Some(on_condition) = &e.on_condition {
24644            self.write_space();
24645            self.generate_expression(on_condition)?;
24646        }
24647        self.write(")");
24648        Ok(())
24649    }
24650
24651    fn generate_json_cast(&mut self, e: &JSONCast) -> Result<()> {
24652        self.generate_expression(&e.this)?;
24653        self.write(".:");
24654        self.generate_data_type(&e.to)?;
24655        Ok(())
24656    }
24657
24658    fn generate_json_extract_array(&mut self, e: &JSONExtractArray) -> Result<()> {
24659        // JSON_EXTRACT_ARRAY(this, expression)
24660        self.write_keyword("JSON_EXTRACT_ARRAY");
24661        self.write("(");
24662        self.generate_expression(&e.this)?;
24663        if let Some(expr) = &e.expression {
24664            self.write(", ");
24665            self.generate_expression(expr)?;
24666        }
24667        self.write(")");
24668        Ok(())
24669    }
24670
24671    fn generate_json_extract_quote(&mut self, e: &JSONExtractQuote) -> Result<()> {
24672        // Snowflake: KEEP [OMIT] QUOTES [SCALAR_ONLY] for JSON extraction
24673        if let Some(option) = &e.option {
24674            self.generate_expression(option)?;
24675            self.write_space();
24676        }
24677        self.write_keyword("QUOTES");
24678        if e.scalar.is_some() {
24679            self.write_keyword(" SCALAR_ONLY");
24680        }
24681        Ok(())
24682    }
24683
24684    fn generate_json_extract_scalar(&mut self, e: &JSONExtractScalar) -> Result<()> {
24685        // JSON_EXTRACT_SCALAR(this, expression)
24686        self.write_keyword("JSON_EXTRACT_SCALAR");
24687        self.write("(");
24688        self.generate_expression(&e.this)?;
24689        self.write(", ");
24690        self.generate_expression(&e.expression)?;
24691        self.write(")");
24692        Ok(())
24693    }
24694
24695    fn generate_json_extract_path(&mut self, e: &JSONExtract) -> Result<()> {
24696        // For variant_extract (Snowflake/Databricks colon syntax like a:field)
24697        // Databricks uses col:path syntax, Snowflake uses GET_PATH(col, 'path')
24698        // Otherwise output JSON_EXTRACT(this, expression)
24699        if e.variant_extract.is_some() {
24700            use crate::dialects::DialectType;
24701            if matches!(self.config.dialect, Some(DialectType::Databricks)) {
24702                // Databricks: output col:path syntax (e.g., c1:price, c1:price.foo, c1:price.bar[1])
24703                self.generate_expression(&e.this)?;
24704                self.write(":");
24705                // The expression is a string literal containing the path (e.g., 'price' or 'price.foo')
24706                // We need to output it without quotes
24707                match e.expression.as_ref() {
24708                    Expression::Literal(Literal::String(s)) => {
24709                        self.write(s);
24710                    }
24711                    _ => {
24712                        // Fallback: generate as-is (shouldn't happen in typical cases)
24713                        self.generate_expression(&e.expression)?;
24714                    }
24715                }
24716            } else {
24717                // Snowflake and others: use GET_PATH(col, 'path')
24718                self.write_keyword("GET_PATH");
24719                self.write("(");
24720                self.generate_expression(&e.this)?;
24721                self.write(", ");
24722                self.generate_expression(&e.expression)?;
24723                self.write(")");
24724            }
24725        } else {
24726            self.write_keyword("JSON_EXTRACT");
24727            self.write("(");
24728            self.generate_expression(&e.this)?;
24729            self.write(", ");
24730            self.generate_expression(&e.expression)?;
24731            for expr in &e.expressions {
24732                self.write(", ");
24733                self.generate_expression(expr)?;
24734            }
24735            self.write(")");
24736        }
24737        Ok(())
24738    }
24739
24740    fn generate_json_format(&mut self, e: &JSONFormat) -> Result<()> {
24741        // Output: {expr} FORMAT JSON
24742        // This wraps an expression with FORMAT JSON suffix (Oracle JSON function syntax)
24743        if let Some(this) = &e.this {
24744            self.generate_expression(this)?;
24745            self.write_space();
24746        }
24747        self.write_keyword("FORMAT JSON");
24748        Ok(())
24749    }
24750
24751    fn generate_json_key_value(&mut self, e: &JSONKeyValue) -> Result<()> {
24752        // key: value (for JSON objects)
24753        self.generate_expression(&e.this)?;
24754        self.write(": ");
24755        self.generate_expression(&e.expression)?;
24756        Ok(())
24757    }
24758
24759    fn generate_json_keys(&mut self, e: &JSONKeys) -> Result<()> {
24760        // JSON_KEYS(this, expression, expressions...)
24761        self.write_keyword("JSON_KEYS");
24762        self.write("(");
24763        self.generate_expression(&e.this)?;
24764        if let Some(expr) = &e.expression {
24765            self.write(", ");
24766            self.generate_expression(expr)?;
24767        }
24768        for expr in &e.expressions {
24769            self.write(", ");
24770            self.generate_expression(expr)?;
24771        }
24772        self.write(")");
24773        Ok(())
24774    }
24775
24776    fn generate_json_keys_at_depth(&mut self, e: &JSONKeysAtDepth) -> Result<()> {
24777        // JSON_KEYS(this, expression)
24778        self.write_keyword("JSON_KEYS");
24779        self.write("(");
24780        self.generate_expression(&e.this)?;
24781        if let Some(expr) = &e.expression {
24782            self.write(", ");
24783            self.generate_expression(expr)?;
24784        }
24785        self.write(")");
24786        Ok(())
24787    }
24788
24789    fn generate_json_path_expr(&mut self, e: &JSONPath) -> Result<()> {
24790        // JSONPath expression: generates a quoted path like '$.foo' or '$[0]'
24791        // The path components are concatenated without spaces
24792        let mut path_str = String::new();
24793        for expr in &e.expressions {
24794            match expr {
24795                Expression::JSONPathRoot(_) => {
24796                    path_str.push('$');
24797                }
24798                Expression::JSONPathKey(k) => {
24799                    // .key or ."key" (quote if key has special characters)
24800                    if let Expression::Literal(crate::expressions::Literal::String(s)) = k.this.as_ref() {
24801                        path_str.push('.');
24802                        // Quote the key if it contains non-alphanumeric characters (hyphens, spaces, etc.)
24803                        let needs_quoting = s.chars().any(|c| !c.is_alphanumeric() && c != '_');
24804                        if needs_quoting {
24805                            path_str.push('"');
24806                            path_str.push_str(s);
24807                            path_str.push('"');
24808                        } else {
24809                            path_str.push_str(s);
24810                        }
24811                    }
24812                }
24813                Expression::JSONPathSubscript(s) => {
24814                    // [index]
24815                    if let Expression::Literal(crate::expressions::Literal::Number(n)) = s.this.as_ref() {
24816                        path_str.push('[');
24817                        path_str.push_str(n);
24818                        path_str.push(']');
24819                    }
24820                }
24821                _ => {
24822                    // For other path parts, try to generate them
24823                    let mut temp_gen = Self::with_config(self.config.clone());
24824                    temp_gen.generate_expression(expr)?;
24825                    path_str.push_str(&temp_gen.output);
24826                }
24827            }
24828        }
24829        // Output as quoted string
24830        self.write("'");
24831        self.write(&path_str);
24832        self.write("'");
24833        Ok(())
24834    }
24835
24836    fn generate_json_path_filter(&mut self, e: &JSONPathFilter) -> Result<()> {
24837        // JSON path filter: ?(predicate)
24838        self.write("?(");
24839        self.generate_expression(&e.this)?;
24840        self.write(")");
24841        Ok(())
24842    }
24843
24844    fn generate_json_path_key(&mut self, e: &JSONPathKey) -> Result<()> {
24845        // JSON path key: .key or ["key"]
24846        self.write(".");
24847        self.generate_expression(&e.this)?;
24848        Ok(())
24849    }
24850
24851    fn generate_json_path_recursive(&mut self, e: &JSONPathRecursive) -> Result<()> {
24852        // JSON path recursive descent: ..
24853        self.write("..");
24854        if let Some(this) = &e.this {
24855            self.generate_expression(this)?;
24856        }
24857        Ok(())
24858    }
24859
24860    fn generate_json_path_root(&mut self) -> Result<()> {
24861        // JSON path root: $
24862        self.write("$");
24863        Ok(())
24864    }
24865
24866    fn generate_json_path_script(&mut self, e: &JSONPathScript) -> Result<()> {
24867        // JSON path script: (expression)
24868        self.write("(");
24869        self.generate_expression(&e.this)?;
24870        self.write(")");
24871        Ok(())
24872    }
24873
24874    fn generate_json_path_selector(&mut self, e: &JSONPathSelector) -> Result<()> {
24875        // JSON path selector: *
24876        self.generate_expression(&e.this)?;
24877        Ok(())
24878    }
24879
24880    fn generate_json_path_slice(&mut self, e: &JSONPathSlice) -> Result<()> {
24881        // JSON path slice: [start:end:step]
24882        self.write("[");
24883        if let Some(start) = &e.start {
24884            self.generate_expression(start)?;
24885        }
24886        self.write(":");
24887        if let Some(end) = &e.end {
24888            self.generate_expression(end)?;
24889        }
24890        if let Some(step) = &e.step {
24891            self.write(":");
24892            self.generate_expression(step)?;
24893        }
24894        self.write("]");
24895        Ok(())
24896    }
24897
24898    fn generate_json_path_subscript(&mut self, e: &JSONPathSubscript) -> Result<()> {
24899        // JSON path subscript: [index] or [*]
24900        self.write("[");
24901        self.generate_expression(&e.this)?;
24902        self.write("]");
24903        Ok(())
24904    }
24905
24906    fn generate_json_path_union(&mut self, e: &JSONPathUnion) -> Result<()> {
24907        // JSON path union: [key1, key2, ...]
24908        self.write("[");
24909        for (i, expr) in e.expressions.iter().enumerate() {
24910            if i > 0 {
24911                self.write(", ");
24912            }
24913            self.generate_expression(expr)?;
24914        }
24915        self.write("]");
24916        Ok(())
24917    }
24918
24919    fn generate_json_remove(&mut self, e: &JSONRemove) -> Result<()> {
24920        // JSON_REMOVE(this, path1, path2, ...)
24921        self.write_keyword("JSON_REMOVE");
24922        self.write("(");
24923        self.generate_expression(&e.this)?;
24924        for expr in &e.expressions {
24925            self.write(", ");
24926            self.generate_expression(expr)?;
24927        }
24928        self.write(")");
24929        Ok(())
24930    }
24931
24932    fn generate_json_schema(&mut self, e: &JSONSchema) -> Result<()> {
24933        // COLUMNS(col1 type, col2 type, ...)
24934        // When pretty printing and content is too wide, format with each column on a separate line
24935        self.write_keyword("COLUMNS");
24936        self.write("(");
24937
24938        if self.config.pretty && !e.expressions.is_empty() {
24939            // First, generate all expressions into strings to check width
24940            let mut expr_strings: Vec<String> = Vec::with_capacity(e.expressions.len());
24941            for expr in &e.expressions {
24942                let mut temp_gen = Generator::with_config(self.config.clone());
24943                temp_gen.generate_expression(expr)?;
24944                expr_strings.push(temp_gen.output);
24945            }
24946
24947            // Check if total width exceeds max_text_width
24948            if self.too_wide(&expr_strings) {
24949                // Pretty print: each column on its own line
24950                self.write_newline();
24951                self.indent_level += 1;
24952                for (i, expr_str) in expr_strings.iter().enumerate() {
24953                    if i > 0 {
24954                        self.write(",");
24955                        self.write_newline();
24956                    }
24957                    self.write_indent();
24958                    self.write(expr_str);
24959                }
24960                self.write_newline();
24961                self.indent_level -= 1;
24962                self.write_indent();
24963            } else {
24964                // Compact: all on one line
24965                for (i, expr_str) in expr_strings.iter().enumerate() {
24966                    if i > 0 {
24967                        self.write(", ");
24968                    }
24969                    self.write(expr_str);
24970                }
24971            }
24972        } else {
24973            // Non-pretty mode: compact format
24974            for (i, expr) in e.expressions.iter().enumerate() {
24975                if i > 0 {
24976                    self.write(", ");
24977                }
24978                self.generate_expression(expr)?;
24979            }
24980        }
24981        self.write(")");
24982        Ok(())
24983    }
24984
24985    fn generate_json_set(&mut self, e: &JSONSet) -> Result<()> {
24986        // JSON_SET(this, path, value, ...)
24987        self.write_keyword("JSON_SET");
24988        self.write("(");
24989        self.generate_expression(&e.this)?;
24990        for expr in &e.expressions {
24991            self.write(", ");
24992            self.generate_expression(expr)?;
24993        }
24994        self.write(")");
24995        Ok(())
24996    }
24997
24998    fn generate_json_strip_nulls(&mut self, e: &JSONStripNulls) -> Result<()> {
24999        // JSON_STRIP_NULLS(this, expression)
25000        self.write_keyword("JSON_STRIP_NULLS");
25001        self.write("(");
25002        self.generate_expression(&e.this)?;
25003        if let Some(expr) = &e.expression {
25004            self.write(", ");
25005            self.generate_expression(expr)?;
25006        }
25007        self.write(")");
25008        Ok(())
25009    }
25010
25011    fn generate_json_table(&mut self, e: &JSONTable) -> Result<()> {
25012        // JSON_TABLE(this, path [error_handling] [empty_handling] schema)
25013        self.write_keyword("JSON_TABLE");
25014        self.write("(");
25015        self.generate_expression(&e.this)?;
25016        if let Some(path) = &e.path {
25017            self.write(", ");
25018            self.generate_expression(path)?;
25019        }
25020        if let Some(error_handling) = &e.error_handling {
25021            self.write_space();
25022            self.generate_expression(error_handling)?;
25023        }
25024        if let Some(empty_handling) = &e.empty_handling {
25025            self.write_space();
25026            self.generate_expression(empty_handling)?;
25027        }
25028        if let Some(schema) = &e.schema {
25029            self.write_space();
25030            self.generate_expression(schema)?;
25031        }
25032        self.write(")");
25033        Ok(())
25034    }
25035
25036    fn generate_json_type(&mut self, e: &JSONType) -> Result<()> {
25037        // JSON_TYPE(this)
25038        self.write_keyword("JSON_TYPE");
25039        self.write("(");
25040        self.generate_expression(&e.this)?;
25041        self.write(")");
25042        Ok(())
25043    }
25044
25045    fn generate_json_value(&mut self, e: &JSONValue) -> Result<()> {
25046        // JSON_VALUE(this, path RETURNING type ON condition)
25047        self.write_keyword("JSON_VALUE");
25048        self.write("(");
25049        self.generate_expression(&e.this)?;
25050        if let Some(path) = &e.path {
25051            self.write(", ");
25052            self.generate_expression(path)?;
25053        }
25054        if let Some(returning) = &e.returning {
25055            self.write_space();
25056            self.write_keyword("RETURNING");
25057            self.write_space();
25058            self.generate_expression(returning)?;
25059        }
25060        if let Some(on_condition) = &e.on_condition {
25061            self.write_space();
25062            self.generate_expression(on_condition)?;
25063        }
25064        self.write(")");
25065        Ok(())
25066    }
25067
25068    fn generate_json_value_array(&mut self, e: &JSONValueArray) -> Result<()> {
25069        // JSON_VALUE_ARRAY(this)
25070        self.write_keyword("JSON_VALUE_ARRAY");
25071        self.write("(");
25072        self.generate_expression(&e.this)?;
25073        self.write(")");
25074        Ok(())
25075    }
25076
25077    fn generate_jarowinkler_similarity(&mut self, e: &JarowinklerSimilarity) -> Result<()> {
25078        // JAROWINKLER_SIMILARITY(str1, str2)
25079        self.write_keyword("JAROWINKLER_SIMILARITY");
25080        self.write("(");
25081        self.generate_expression(&e.this)?;
25082        self.write(", ");
25083        self.generate_expression(&e.expression)?;
25084        self.write(")");
25085        Ok(())
25086    }
25087
25088    fn generate_join_hint(&mut self, e: &JoinHint) -> Result<()> {
25089        // Python: this(expressions)
25090        self.generate_expression(&e.this)?;
25091        self.write("(");
25092        for (i, expr) in e.expressions.iter().enumerate() {
25093            if i > 0 {
25094                self.write(", ");
25095            }
25096            self.generate_expression(expr)?;
25097        }
25098        self.write(")");
25099        Ok(())
25100    }
25101
25102    fn generate_journal_property(&mut self, e: &JournalProperty) -> Result<()> {
25103        // Python: {no}{local}{dual}{before}{after}JOURNAL
25104        if e.no.is_some() {
25105            self.write_keyword("NO ");
25106        }
25107        if let Some(local) = &e.local {
25108            self.generate_expression(local)?;
25109            self.write_space();
25110        }
25111        if e.dual.is_some() {
25112            self.write_keyword("DUAL ");
25113        }
25114        if e.before.is_some() {
25115            self.write_keyword("BEFORE ");
25116        }
25117        if e.after.is_some() {
25118            self.write_keyword("AFTER ");
25119        }
25120        self.write_keyword("JOURNAL");
25121        Ok(())
25122    }
25123
25124    fn generate_language_property(&mut self, e: &LanguageProperty) -> Result<()> {
25125        // LANGUAGE language_name
25126        self.write_keyword("LANGUAGE");
25127        self.write_space();
25128        self.generate_expression(&e.this)?;
25129        Ok(())
25130    }
25131
25132    fn generate_lateral(&mut self, e: &Lateral) -> Result<()> {
25133        // Python: handles LATERAL VIEW (Hive/Spark) and regular LATERAL
25134        if e.view.is_some() {
25135            // LATERAL VIEW [OUTER] expression [alias] [AS columns]
25136            self.write_keyword("LATERAL VIEW");
25137            if e.outer.is_some() {
25138                self.write_space();
25139                self.write_keyword("OUTER");
25140            }
25141            self.write_space();
25142            self.generate_expression(&e.this)?;
25143            if let Some(alias) = &e.alias {
25144                self.write_space();
25145                self.write(alias);
25146            }
25147        } else {
25148            // LATERAL subquery/function [WITH ORDINALITY] [AS alias(columns)]
25149            self.write_keyword("LATERAL");
25150            self.write_space();
25151            self.generate_expression(&e.this)?;
25152            if e.ordinality.is_some() {
25153                self.write_space();
25154                self.write_keyword("WITH ORDINALITY");
25155            }
25156            if let Some(alias) = &e.alias {
25157                self.write_space();
25158                self.write_keyword("AS");
25159                self.write_space();
25160                self.write(alias);
25161                if !e.column_aliases.is_empty() {
25162                    self.write("(");
25163                    for (i, col) in e.column_aliases.iter().enumerate() {
25164                        if i > 0 {
25165                            self.write(", ");
25166                        }
25167                        self.write(col);
25168                    }
25169                    self.write(")");
25170                }
25171            }
25172        }
25173        Ok(())
25174    }
25175
25176    fn generate_like_property(&mut self, e: &LikeProperty) -> Result<()> {
25177        // Python: LIKE this [options]
25178        self.write_keyword("LIKE");
25179        self.write_space();
25180        self.generate_expression(&e.this)?;
25181        for expr in &e.expressions {
25182            self.write_space();
25183            self.generate_expression(expr)?;
25184        }
25185        Ok(())
25186    }
25187
25188    fn generate_limit(&mut self, e: &Limit) -> Result<()> {
25189        self.write_keyword("LIMIT");
25190        self.write_space();
25191        self.write_limit_expr(&e.this)?;
25192        if e.percent {
25193            self.write_space();
25194            self.write_keyword("PERCENT");
25195        }
25196        Ok(())
25197    }
25198
25199    fn generate_limit_options(&mut self, e: &LimitOptions) -> Result<()> {
25200        // Python: [PERCENT][ROWS][WITH TIES|ONLY]
25201        if e.percent.is_some() {
25202            self.write_keyword(" PERCENT");
25203        }
25204        if e.rows.is_some() {
25205            self.write_keyword(" ROWS");
25206        }
25207        if e.with_ties.is_some() {
25208            self.write_keyword(" WITH TIES");
25209        } else if e.rows.is_some() {
25210            self.write_keyword(" ONLY");
25211        }
25212        Ok(())
25213    }
25214
25215    fn generate_list(&mut self, e: &List) -> Result<()> {
25216        use crate::dialects::DialectType;
25217        let is_materialize = matches!(self.config.dialect, Some(DialectType::Materialize));
25218
25219        // Check if this is a subquery-based list (LIST(SELECT ...))
25220        if e.expressions.len() == 1 {
25221            if let Expression::Select(_) = &e.expressions[0] {
25222                self.write_keyword("LIST");
25223                self.write("(");
25224                self.generate_expression(&e.expressions[0])?;
25225                self.write(")");
25226                return Ok(());
25227            }
25228        }
25229
25230        // For Materialize, output as LIST[expr, expr, ...]
25231        if is_materialize {
25232            self.write_keyword("LIST");
25233            self.write("[");
25234            for (i, expr) in e.expressions.iter().enumerate() {
25235                if i > 0 {
25236                    self.write(", ");
25237                }
25238                self.generate_expression(expr)?;
25239            }
25240            self.write("]");
25241        } else {
25242            // For other dialects, output as LIST(expr, expr, ...)
25243            self.write_keyword("LIST");
25244            self.write("(");
25245            for (i, expr) in e.expressions.iter().enumerate() {
25246                if i > 0 {
25247                    self.write(", ");
25248                }
25249                self.generate_expression(expr)?;
25250            }
25251            self.write(")");
25252        }
25253        Ok(())
25254    }
25255
25256    fn generate_tomap(&mut self, e: &ToMap) -> Result<()> {
25257        // Check if this is a subquery-based map (MAP(SELECT ...))
25258        if let Expression::Select(_) = &*e.this {
25259            self.write_keyword("MAP");
25260            self.write("(");
25261            self.generate_expression(&e.this)?;
25262            self.write(")");
25263            return Ok(());
25264        }
25265
25266        let is_duckdb = matches!(self.config.dialect, Some(DialectType::DuckDB));
25267
25268        // For Struct-based map: DuckDB uses MAP {'key': value}, Materialize uses MAP['key' => value]
25269        self.write_keyword("MAP");
25270        if is_duckdb {
25271            self.write(" {");
25272        } else {
25273            self.write("[");
25274        }
25275        if let Expression::Struct(s) = &*e.this {
25276            for (i, (_, expr)) in s.fields.iter().enumerate() {
25277                if i > 0 {
25278                    self.write(", ");
25279                }
25280                if let Expression::PropertyEQ(op) = expr {
25281                    self.generate_expression(&op.left)?;
25282                    if is_duckdb {
25283                        self.write(": ");
25284                    } else {
25285                        self.write(" => ");
25286                    }
25287                    self.generate_expression(&op.right)?;
25288                } else {
25289                    self.generate_expression(expr)?;
25290                }
25291            }
25292        }
25293        if is_duckdb {
25294            self.write("}");
25295        } else {
25296            self.write("]");
25297        }
25298        Ok(())
25299    }
25300
25301    fn generate_localtime(&mut self, e: &Localtime) -> Result<()> {
25302        // Python: LOCALTIME or LOCALTIME(precision)
25303        self.write_keyword("LOCALTIME");
25304        if let Some(precision) = &e.this {
25305            self.write("(");
25306            self.generate_expression(precision)?;
25307            self.write(")");
25308        }
25309        Ok(())
25310    }
25311
25312    fn generate_localtimestamp(&mut self, e: &Localtimestamp) -> Result<()> {
25313        // Python: LOCALTIMESTAMP or LOCALTIMESTAMP(precision)
25314        self.write_keyword("LOCALTIMESTAMP");
25315        if let Some(precision) = &e.this {
25316            self.write("(");
25317            self.generate_expression(precision)?;
25318            self.write(")");
25319        }
25320        Ok(())
25321    }
25322
25323    fn generate_location_property(&mut self, e: &LocationProperty) -> Result<()> {
25324        // LOCATION 'path'
25325        self.write_keyword("LOCATION");
25326        self.write_space();
25327        self.generate_expression(&e.this)?;
25328        Ok(())
25329    }
25330
25331    fn generate_lock(&mut self, e: &Lock) -> Result<()> {
25332        // Python: FOR UPDATE|FOR SHARE [OF tables] [NOWAIT|WAIT n]
25333        if e.update.is_some() {
25334            if e.key.is_some() {
25335                self.write_keyword("FOR NO KEY UPDATE");
25336            } else {
25337                self.write_keyword("FOR UPDATE");
25338            }
25339        } else {
25340            if e.key.is_some() {
25341                self.write_keyword("FOR KEY SHARE");
25342            } else {
25343                self.write_keyword("FOR SHARE");
25344            }
25345        }
25346        if !e.expressions.is_empty() {
25347            self.write_keyword(" OF ");
25348            for (i, expr) in e.expressions.iter().enumerate() {
25349                if i > 0 {
25350                    self.write(", ");
25351                }
25352                self.generate_expression(expr)?;
25353            }
25354        }
25355        // Handle wait option following Python sqlglot convention:
25356        // - Boolean(true) -> NOWAIT
25357        // - Boolean(false) -> SKIP LOCKED
25358        // - Literal (number) -> WAIT n
25359        if let Some(wait) = &e.wait {
25360            match wait.as_ref() {
25361                Expression::Boolean(b) => {
25362                    if b.value {
25363                        self.write_keyword(" NOWAIT");
25364                    } else {
25365                        self.write_keyword(" SKIP LOCKED");
25366                    }
25367                }
25368                _ => {
25369                    // It's a literal (number), output WAIT n
25370                    self.write_keyword(" WAIT ");
25371                    self.generate_expression(wait)?;
25372                }
25373            }
25374        }
25375        Ok(())
25376    }
25377
25378    fn generate_lock_property(&mut self, e: &LockProperty) -> Result<()> {
25379        // LOCK property
25380        self.write_keyword("LOCK");
25381        self.write_space();
25382        self.generate_expression(&e.this)?;
25383        Ok(())
25384    }
25385
25386    fn generate_locking_property(&mut self, e: &LockingProperty) -> Result<()> {
25387        // Python: LOCKING kind [this] [for_or_in] lock_type [OVERRIDE]
25388        self.write_keyword("LOCKING");
25389        self.write_space();
25390        self.write(&e.kind);
25391        if let Some(this) = &e.this {
25392            self.write_space();
25393            self.generate_expression(this)?;
25394        }
25395        if let Some(for_or_in) = &e.for_or_in {
25396            self.write_space();
25397            self.generate_expression(for_or_in)?;
25398        }
25399        if let Some(lock_type) = &e.lock_type {
25400            self.write_space();
25401            self.generate_expression(lock_type)?;
25402        }
25403        if e.override_.is_some() {
25404            self.write_keyword(" OVERRIDE");
25405        }
25406        Ok(())
25407    }
25408
25409    fn generate_locking_statement(&mut self, e: &LockingStatement) -> Result<()> {
25410        // this expression
25411        self.generate_expression(&e.this)?;
25412        self.write_space();
25413        self.generate_expression(&e.expression)?;
25414        Ok(())
25415    }
25416
25417    fn generate_log_property(&mut self, e: &LogProperty) -> Result<()> {
25418        // [NO] LOG
25419        if e.no.is_some() {
25420            self.write_keyword("NO ");
25421        }
25422        self.write_keyword("LOG");
25423        Ok(())
25424    }
25425
25426    fn generate_md5_digest(&mut self, e: &MD5Digest) -> Result<()> {
25427        // MD5(this, expressions...)
25428        self.write_keyword("MD5");
25429        self.write("(");
25430        self.generate_expression(&e.this)?;
25431        for expr in &e.expressions {
25432            self.write(", ");
25433            self.generate_expression(expr)?;
25434        }
25435        self.write(")");
25436        Ok(())
25437    }
25438
25439    fn generate_ml_forecast(&mut self, e: &MLForecast) -> Result<()> {
25440        // ML.FORECAST(model, [params])
25441        self.write_keyword("ML.FORECAST");
25442        self.write("(");
25443        self.generate_expression(&e.this)?;
25444        if let Some(expression) = &e.expression {
25445            self.write(", ");
25446            self.generate_expression(expression)?;
25447        }
25448        if let Some(params) = &e.params_struct {
25449            self.write(", ");
25450            self.generate_expression(params)?;
25451        }
25452        self.write(")");
25453        Ok(())
25454    }
25455
25456    fn generate_ml_translate(&mut self, e: &MLTranslate) -> Result<()> {
25457        // ML.TRANSLATE(model, input, [params])
25458        self.write_keyword("ML.TRANSLATE");
25459        self.write("(");
25460        self.generate_expression(&e.this)?;
25461        self.write(", ");
25462        self.generate_expression(&e.expression)?;
25463        if let Some(params) = &e.params_struct {
25464            self.write(", ");
25465            self.generate_expression(params)?;
25466        }
25467        self.write(")");
25468        Ok(())
25469    }
25470
25471    fn generate_make_interval(&mut self, e: &MakeInterval) -> Result<()> {
25472        // MAKE_INTERVAL(years => x, months => y, ...)
25473        self.write_keyword("MAKE_INTERVAL");
25474        self.write("(");
25475        let mut first = true;
25476        if let Some(year) = &e.year {
25477            self.write("years => ");
25478            self.generate_expression(year)?;
25479            first = false;
25480        }
25481        if let Some(month) = &e.month {
25482            if !first { self.write(", "); }
25483            self.write("months => ");
25484            self.generate_expression(month)?;
25485            first = false;
25486        }
25487        if let Some(week) = &e.week {
25488            if !first { self.write(", "); }
25489            self.write("weeks => ");
25490            self.generate_expression(week)?;
25491            first = false;
25492        }
25493        if let Some(day) = &e.day {
25494            if !first { self.write(", "); }
25495            self.write("days => ");
25496            self.generate_expression(day)?;
25497            first = false;
25498        }
25499        if let Some(hour) = &e.hour {
25500            if !first { self.write(", "); }
25501            self.write("hours => ");
25502            self.generate_expression(hour)?;
25503            first = false;
25504        }
25505        if let Some(minute) = &e.minute {
25506            if !first { self.write(", "); }
25507            self.write("mins => ");
25508            self.generate_expression(minute)?;
25509            first = false;
25510        }
25511        if let Some(second) = &e.second {
25512            if !first { self.write(", "); }
25513            self.write("secs => ");
25514            self.generate_expression(second)?;
25515        }
25516        self.write(")");
25517        Ok(())
25518    }
25519
25520    fn generate_manhattan_distance(&mut self, e: &ManhattanDistance) -> Result<()> {
25521        // MANHATTAN_DISTANCE(vector1, vector2)
25522        self.write_keyword("MANHATTAN_DISTANCE");
25523        self.write("(");
25524        self.generate_expression(&e.this)?;
25525        self.write(", ");
25526        self.generate_expression(&e.expression)?;
25527        self.write(")");
25528        Ok(())
25529    }
25530
25531    fn generate_map(&mut self, e: &Map) -> Result<()> {
25532        // MAP(key1, value1, key2, value2, ...)
25533        self.write_keyword("MAP");
25534        self.write("(");
25535        for (i, (key, value)) in e.keys.iter().zip(e.values.iter()).enumerate() {
25536            if i > 0 {
25537                self.write(", ");
25538            }
25539            self.generate_expression(key)?;
25540            self.write(", ");
25541            self.generate_expression(value)?;
25542        }
25543        self.write(")");
25544        Ok(())
25545    }
25546
25547    fn generate_map_cat(&mut self, e: &MapCat) -> Result<()> {
25548        // MAP_CAT(map1, map2)
25549        self.write_keyword("MAP_CAT");
25550        self.write("(");
25551        self.generate_expression(&e.this)?;
25552        self.write(", ");
25553        self.generate_expression(&e.expression)?;
25554        self.write(")");
25555        Ok(())
25556    }
25557
25558    fn generate_map_delete(&mut self, e: &MapDelete) -> Result<()> {
25559        // MAP_DELETE(map, key1, key2, ...)
25560        self.write_keyword("MAP_DELETE");
25561        self.write("(");
25562        self.generate_expression(&e.this)?;
25563        for expr in &e.expressions {
25564            self.write(", ");
25565            self.generate_expression(expr)?;
25566        }
25567        self.write(")");
25568        Ok(())
25569    }
25570
25571    fn generate_map_insert(&mut self, e: &MapInsert) -> Result<()> {
25572        // MAP_INSERT(map, key, value, [update_flag])
25573        self.write_keyword("MAP_INSERT");
25574        self.write("(");
25575        self.generate_expression(&e.this)?;
25576        if let Some(key) = &e.key {
25577            self.write(", ");
25578            self.generate_expression(key)?;
25579        }
25580        if let Some(value) = &e.value {
25581            self.write(", ");
25582            self.generate_expression(value)?;
25583        }
25584        if let Some(update_flag) = &e.update_flag {
25585            self.write(", ");
25586            self.generate_expression(update_flag)?;
25587        }
25588        self.write(")");
25589        Ok(())
25590    }
25591
25592    fn generate_map_pick(&mut self, e: &MapPick) -> Result<()> {
25593        // MAP_PICK(map, key1, key2, ...)
25594        self.write_keyword("MAP_PICK");
25595        self.write("(");
25596        self.generate_expression(&e.this)?;
25597        for expr in &e.expressions {
25598            self.write(", ");
25599            self.generate_expression(expr)?;
25600        }
25601        self.write(")");
25602        Ok(())
25603    }
25604
25605    fn generate_masking_policy_column_constraint(&mut self, e: &MaskingPolicyColumnConstraint) -> Result<()> {
25606        // Python: MASKING POLICY name [USING (cols)]
25607        self.write_keyword("MASKING POLICY");
25608        self.write_space();
25609        self.generate_expression(&e.this)?;
25610        if !e.expressions.is_empty() {
25611            self.write_keyword(" USING");
25612            self.write(" (");
25613            for (i, expr) in e.expressions.iter().enumerate() {
25614                if i > 0 {
25615                    self.write(", ");
25616                }
25617                self.generate_expression(expr)?;
25618            }
25619            self.write(")");
25620        }
25621        Ok(())
25622    }
25623
25624    fn generate_match_against(&mut self, e: &MatchAgainst) -> Result<()> {
25625        if matches!(
25626            self.config.dialect,
25627            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)
25628        ) {
25629            if e.expressions.len() > 1 {
25630                self.write("(");
25631            }
25632            for (i, expr) in e.expressions.iter().enumerate() {
25633                if i > 0 {
25634                    self.write_keyword(" OR ");
25635                }
25636                self.generate_expression(expr)?;
25637                self.write_space();
25638                self.write("@@");
25639                self.write_space();
25640                self.generate_expression(&e.this)?;
25641            }
25642            if e.expressions.len() > 1 {
25643                self.write(")");
25644            }
25645            return Ok(());
25646        }
25647
25648        // MATCH(columns) AGAINST(expr [modifier])
25649        self.write_keyword("MATCH");
25650        self.write("(");
25651        for (i, expr) in e.expressions.iter().enumerate() {
25652            if i > 0 {
25653                self.write(", ");
25654            }
25655            self.generate_expression(expr)?;
25656        }
25657        self.write(")");
25658        self.write_keyword(" AGAINST");
25659        self.write("(");
25660        self.generate_expression(&e.this)?;
25661        if let Some(modifier) = &e.modifier {
25662            self.write_space();
25663            self.generate_expression(modifier)?;
25664        }
25665        self.write(")");
25666        Ok(())
25667    }
25668
25669    fn generate_match_recognize_measure(&mut self, e: &MatchRecognizeMeasure) -> Result<()> {
25670        // Python: [window_frame] this
25671        if let Some(window_frame) = &e.window_frame {
25672            self.write(&format!("{:?}", window_frame).to_uppercase());
25673            self.write_space();
25674        }
25675        self.generate_expression(&e.this)?;
25676        Ok(())
25677    }
25678
25679    fn generate_materialized_property(&mut self, e: &MaterializedProperty) -> Result<()> {
25680        // MATERIALIZED [this]
25681        self.write_keyword("MATERIALIZED");
25682        if let Some(this) = &e.this {
25683            self.write_space();
25684            self.generate_expression(this)?;
25685        }
25686        Ok(())
25687    }
25688
25689    fn generate_merge(&mut self, e: &Merge) -> Result<()> {
25690        // MERGE INTO target USING source ON condition WHEN ...
25691        // DuckDB variant: MERGE INTO target USING source USING (key_columns) WHEN ...
25692        if let Some(with_) = &e.with_ {
25693            self.generate_expression(with_)?;
25694            self.write_space();
25695        }
25696        self.write_keyword("MERGE INTO");
25697        self.write_space();
25698        self.generate_expression(&e.this)?;
25699
25700        // USING clause - newline before in pretty mode
25701        if self.config.pretty {
25702            self.write_newline();
25703            self.write_indent();
25704        } else {
25705            self.write_space();
25706        }
25707        self.write_keyword("USING");
25708        self.write_space();
25709        self.generate_expression(&e.using)?;
25710
25711        // ON clause - newline before in pretty mode
25712        if let Some(on) = &e.on {
25713            if self.config.pretty {
25714                self.write_newline();
25715                self.write_indent();
25716            } else {
25717                self.write_space();
25718            }
25719            self.write_keyword("ON");
25720            self.write_space();
25721            self.generate_expression(on)?;
25722        }
25723        // DuckDB USING (key_columns) clause
25724        if let Some(using_cond) = &e.using_cond {
25725            self.write_space();
25726            self.write_keyword("USING");
25727            self.write_space();
25728            self.write("(");
25729            // using_cond is a Tuple containing the column identifiers
25730            if let Expression::Tuple(tuple) = using_cond.as_ref() {
25731                for (i, col) in tuple.expressions.iter().enumerate() {
25732                    if i > 0 {
25733                        self.write(", ");
25734                    }
25735                    self.generate_expression(col)?;
25736                }
25737            } else {
25738                self.generate_expression(using_cond)?;
25739            }
25740            self.write(")");
25741        }
25742        // For PostgreSQL dialect, extract target table name/alias to strip from UPDATE SET
25743        let saved_merge_strip = std::mem::take(&mut self.merge_strip_qualifiers);
25744        if matches!(self.config.dialect, Some(crate::DialectType::PostgreSQL) | Some(crate::DialectType::Redshift) | Some(crate::DialectType::Trino) | Some(crate::DialectType::Presto) | Some(crate::DialectType::Athena)) {
25745            let mut names = Vec::new();
25746            match e.this.as_ref() {
25747                Expression::Alias(a) => {
25748                    // e.g., "x AS z" -> strip both "x" and "z"
25749                    if let Expression::Table(t) = &a.this {
25750                        names.push(t.name.name.clone());
25751                    } else if let Expression::Identifier(id) = &a.this {
25752                        names.push(id.name.clone());
25753                    }
25754                    names.push(a.alias.name.clone());
25755                }
25756                Expression::Table(t) => {
25757                    names.push(t.name.name.clone());
25758                }
25759                Expression::Identifier(id) => {
25760                    names.push(id.name.clone());
25761                }
25762                _ => {}
25763            }
25764            self.merge_strip_qualifiers = names;
25765        }
25766
25767        // WHEN clauses - newline before each in pretty mode
25768        if let Some(whens) = &e.whens {
25769            if self.config.pretty {
25770                self.write_newline();
25771                self.write_indent();
25772            } else {
25773                self.write_space();
25774            }
25775            self.generate_expression(whens)?;
25776        }
25777
25778        // Restore merge_strip_qualifiers
25779        self.merge_strip_qualifiers = saved_merge_strip;
25780
25781        // OUTPUT/RETURNING clause - newline before in pretty mode
25782        if let Some(returning) = &e.returning {
25783            if self.config.pretty {
25784                self.write_newline();
25785                self.write_indent();
25786            } else {
25787                self.write_space();
25788            }
25789            self.generate_expression(returning)?;
25790        }
25791        Ok(())
25792    }
25793
25794    fn generate_merge_block_ratio_property(&mut self, e: &MergeBlockRatioProperty) -> Result<()> {
25795        // Python: NO MERGEBLOCKRATIO | DEFAULT MERGEBLOCKRATIO | MERGEBLOCKRATIO=this [PERCENT]
25796        if e.no.is_some() {
25797            self.write_keyword("NO MERGEBLOCKRATIO");
25798        } else if e.default.is_some() {
25799            self.write_keyword("DEFAULT MERGEBLOCKRATIO");
25800        } else {
25801            self.write_keyword("MERGEBLOCKRATIO");
25802            self.write("=");
25803            if let Some(this) = &e.this {
25804                self.generate_expression(this)?;
25805            }
25806            if e.percent.is_some() {
25807                self.write_keyword(" PERCENT");
25808            }
25809        }
25810        Ok(())
25811    }
25812
25813    fn generate_merge_tree_ttl(&mut self, e: &MergeTreeTTL) -> Result<()> {
25814        // TTL expressions [WHERE where] [GROUP BY group] [SET aggregates]
25815        self.write_keyword("TTL");
25816        let pretty_clickhouse = self.config.pretty
25817            && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse));
25818
25819        if pretty_clickhouse {
25820            self.write_newline();
25821            self.indent_level += 1;
25822            for (i, expr) in e.expressions.iter().enumerate() {
25823                if i > 0 {
25824                    self.write(",");
25825                    self.write_newline();
25826                }
25827                self.write_indent();
25828                self.generate_expression(expr)?;
25829            }
25830            self.indent_level -= 1;
25831        } else {
25832            self.write_space();
25833            for (i, expr) in e.expressions.iter().enumerate() {
25834                if i > 0 {
25835                    self.write(", ");
25836                }
25837                self.generate_expression(expr)?;
25838            }
25839        }
25840
25841        if let Some(where_) = &e.where_ {
25842            if pretty_clickhouse {
25843                self.write_newline();
25844                if let Expression::Where(w) = where_.as_ref() {
25845                    self.write_indent();
25846                    self.write_keyword("WHERE");
25847                    self.write_newline();
25848                    self.indent_level += 1;
25849                    self.write_indent();
25850                    self.generate_expression(&w.this)?;
25851                    self.indent_level -= 1;
25852                } else {
25853                    self.write_indent();
25854                    self.generate_expression(where_)?;
25855                }
25856            } else {
25857                self.write_space();
25858                self.generate_expression(where_)?;
25859            }
25860        }
25861        if let Some(group) = &e.group {
25862            if pretty_clickhouse {
25863                self.write_newline();
25864                if let Expression::Group(g) = group.as_ref() {
25865                    self.write_indent();
25866                    self.write_keyword("GROUP BY");
25867                    self.write_newline();
25868                    self.indent_level += 1;
25869                    for (i, expr) in g.expressions.iter().enumerate() {
25870                        if i > 0 {
25871                            self.write(",");
25872                            self.write_newline();
25873                        }
25874                        self.write_indent();
25875                        self.generate_expression(expr)?;
25876                    }
25877                    self.indent_level -= 1;
25878                } else {
25879                    self.write_indent();
25880                    self.generate_expression(group)?;
25881                }
25882            } else {
25883                self.write_space();
25884                self.generate_expression(group)?;
25885            }
25886        }
25887        if let Some(aggregates) = &e.aggregates {
25888            if pretty_clickhouse {
25889                self.write_newline();
25890                self.write_indent();
25891                self.write_keyword("SET");
25892                self.write_newline();
25893                self.indent_level += 1;
25894                if let Expression::Tuple(t) = aggregates.as_ref() {
25895                    for (i, agg) in t.expressions.iter().enumerate() {
25896                        if i > 0 {
25897                            self.write(",");
25898                            self.write_newline();
25899                        }
25900                        self.write_indent();
25901                        self.generate_expression(agg)?;
25902                    }
25903                } else {
25904                    self.write_indent();
25905                    self.generate_expression(aggregates)?;
25906                }
25907                self.indent_level -= 1;
25908            } else {
25909                self.write_space();
25910                self.write_keyword("SET");
25911                self.write_space();
25912                self.generate_expression(aggregates)?;
25913            }
25914        }
25915        Ok(())
25916    }
25917
25918    fn generate_merge_tree_ttl_action(&mut self, e: &MergeTreeTTLAction) -> Result<()> {
25919        // Python: this [DELETE] [RECOMPRESS codec] [TO DISK disk] [TO VOLUME volume]
25920        self.generate_expression(&e.this)?;
25921        if e.delete.is_some() {
25922            self.write_keyword(" DELETE");
25923        }
25924        if let Some(recompress) = &e.recompress {
25925            self.write_keyword(" RECOMPRESS ");
25926            self.generate_expression(recompress)?;
25927        }
25928        if let Some(to_disk) = &e.to_disk {
25929            self.write_keyword(" TO DISK ");
25930            self.generate_expression(to_disk)?;
25931        }
25932        if let Some(to_volume) = &e.to_volume {
25933            self.write_keyword(" TO VOLUME ");
25934            self.generate_expression(to_volume)?;
25935        }
25936        Ok(())
25937    }
25938
25939    fn generate_minhash(&mut self, e: &Minhash) -> Result<()> {
25940        // MINHASH(this, expressions...)
25941        self.write_keyword("MINHASH");
25942        self.write("(");
25943        self.generate_expression(&e.this)?;
25944        for expr in &e.expressions {
25945            self.write(", ");
25946            self.generate_expression(expr)?;
25947        }
25948        self.write(")");
25949        Ok(())
25950    }
25951
25952    fn generate_model_attribute(&mut self, e: &ModelAttribute) -> Result<()> {
25953        // model!attribute - Snowflake syntax
25954        self.generate_expression(&e.this)?;
25955        self.write("!");
25956        self.generate_expression(&e.expression)?;
25957        Ok(())
25958    }
25959
25960    fn generate_monthname(&mut self, e: &Monthname) -> Result<()> {
25961        // MONTHNAME(this)
25962        self.write_keyword("MONTHNAME");
25963        self.write("(");
25964        self.generate_expression(&e.this)?;
25965        self.write(")");
25966        Ok(())
25967    }
25968
25969    fn generate_multitable_inserts(&mut self, e: &MultitableInserts) -> Result<()> {
25970        // Output leading comments
25971        for comment in &e.leading_comments {
25972            self.write_formatted_comment(comment);
25973            self.write_space();
25974        }
25975        // Python: INSERT kind expressions source
25976        self.write_keyword("INSERT");
25977        self.write_space();
25978        self.write(&e.kind);
25979        for expr in &e.expressions {
25980            self.write_space();
25981            self.generate_expression(expr)?;
25982        }
25983        if let Some(source) = &e.source {
25984            self.write_space();
25985            self.generate_expression(source)?;
25986        }
25987        Ok(())
25988    }
25989
25990    fn generate_next_value_for(&mut self, e: &NextValueFor) -> Result<()> {
25991        // Python: NEXT VALUE FOR this [OVER (order)]
25992        self.write_keyword("NEXT VALUE FOR");
25993        self.write_space();
25994        self.generate_expression(&e.this)?;
25995        if let Some(order) = &e.order {
25996            self.write_space();
25997            self.write_keyword("OVER");
25998            self.write(" (");
25999            self.generate_expression(order)?;
26000            self.write(")");
26001        }
26002        Ok(())
26003    }
26004
26005    fn generate_normal(&mut self, e: &Normal) -> Result<()> {
26006        // NORMAL(mean, stddev, gen)
26007        self.write_keyword("NORMAL");
26008        self.write("(");
26009        self.generate_expression(&e.this)?;
26010        if let Some(stddev) = &e.stddev {
26011            self.write(", ");
26012            self.generate_expression(stddev)?;
26013        }
26014        if let Some(gen) = &e.gen {
26015            self.write(", ");
26016            self.generate_expression(gen)?;
26017        }
26018        self.write(")");
26019        Ok(())
26020    }
26021
26022    fn generate_normalize(&mut self, e: &Normalize) -> Result<()> {
26023        // NORMALIZE(this, form) or CASEFOLD version
26024        if e.is_casefold.is_some() {
26025            self.write_keyword("NORMALIZE_AND_CASEFOLD");
26026        } else {
26027            self.write_keyword("NORMALIZE");
26028        }
26029        self.write("(");
26030        self.generate_expression(&e.this)?;
26031        if let Some(form) = &e.form {
26032            self.write(", ");
26033            self.generate_expression(form)?;
26034        }
26035        self.write(")");
26036        Ok(())
26037    }
26038
26039    fn generate_not_null_column_constraint(&mut self, e: &NotNullColumnConstraint) -> Result<()> {
26040        // Python: [NOT ]NULL
26041        if e.allow_null.is_none() {
26042            self.write_keyword("NOT ");
26043        }
26044        self.write_keyword("NULL");
26045        Ok(())
26046    }
26047
26048    fn generate_nullif(&mut self, e: &Nullif) -> Result<()> {
26049        // NULLIF(this, expression)
26050        self.write_keyword("NULLIF");
26051        self.write("(");
26052        self.generate_expression(&e.this)?;
26053        self.write(", ");
26054        self.generate_expression(&e.expression)?;
26055        self.write(")");
26056        Ok(())
26057    }
26058
26059    fn generate_number_to_str(&mut self, e: &NumberToStr) -> Result<()> {
26060        // FORMAT(this, format, culture)
26061        self.write_keyword("FORMAT");
26062        self.write("(");
26063        self.generate_expression(&e.this)?;
26064        self.write(", '");
26065        self.write(&e.format);
26066        self.write("'");
26067        if let Some(culture) = &e.culture {
26068            self.write(", ");
26069            self.generate_expression(culture)?;
26070        }
26071        self.write(")");
26072        Ok(())
26073    }
26074
26075    fn generate_object_agg(&mut self, e: &ObjectAgg) -> Result<()> {
26076        // OBJECT_AGG(key, value)
26077        self.write_keyword("OBJECT_AGG");
26078        self.write("(");
26079        self.generate_expression(&e.this)?;
26080        self.write(", ");
26081        self.generate_expression(&e.expression)?;
26082        self.write(")");
26083        Ok(())
26084    }
26085
26086    fn generate_object_identifier(&mut self, e: &ObjectIdentifier) -> Result<()> {
26087        // Python: Just returns the name
26088        self.generate_expression(&e.this)?;
26089        Ok(())
26090    }
26091
26092    fn generate_object_insert(&mut self, e: &ObjectInsert) -> Result<()> {
26093        // OBJECT_INSERT(obj, key, value, [update_flag])
26094        self.write_keyword("OBJECT_INSERT");
26095        self.write("(");
26096        self.generate_expression(&e.this)?;
26097        if let Some(key) = &e.key {
26098            self.write(", ");
26099            self.generate_expression(key)?;
26100        }
26101        if let Some(value) = &e.value {
26102            self.write(", ");
26103            self.generate_expression(value)?;
26104        }
26105        if let Some(update_flag) = &e.update_flag {
26106            self.write(", ");
26107            self.generate_expression(update_flag)?;
26108        }
26109        self.write(")");
26110        Ok(())
26111    }
26112
26113    fn generate_offset(&mut self, e: &Offset) -> Result<()> {
26114        // OFFSET value [ROW|ROWS]
26115        self.write_keyword("OFFSET");
26116        self.write_space();
26117        self.generate_expression(&e.this)?;
26118        // Output ROWS keyword only for TSQL/Oracle targets
26119        if e.rows == Some(true) && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Oracle)) {
26120            self.write_space();
26121            self.write_keyword("ROWS");
26122        }
26123        Ok(())
26124    }
26125
26126    fn generate_qualify(&mut self, e: &Qualify) -> Result<()> {
26127        // QUALIFY condition (Snowflake/BigQuery)
26128        self.write_keyword("QUALIFY");
26129        self.write_space();
26130        self.generate_expression(&e.this)?;
26131        Ok(())
26132    }
26133
26134    fn generate_on_cluster(&mut self, e: &OnCluster) -> Result<()> {
26135        // ON CLUSTER cluster_name
26136        self.write_keyword("ON CLUSTER");
26137        self.write_space();
26138        self.generate_expression(&e.this)?;
26139        Ok(())
26140    }
26141
26142    fn generate_on_commit_property(&mut self, e: &OnCommitProperty) -> Result<()> {
26143        // ON COMMIT [DELETE ROWS | PRESERVE ROWS]
26144        self.write_keyword("ON COMMIT");
26145        if e.delete.is_some() {
26146            self.write_keyword(" DELETE ROWS");
26147        } else {
26148            self.write_keyword(" PRESERVE ROWS");
26149        }
26150        Ok(())
26151    }
26152
26153    fn generate_on_condition(&mut self, e: &OnCondition) -> Result<()> {
26154        // Python: error/empty/null handling
26155        if let Some(empty) = &e.empty {
26156            self.generate_expression(empty)?;
26157            self.write_keyword(" ON EMPTY");
26158        }
26159        if let Some(error) = &e.error {
26160            if e.empty.is_some() {
26161                self.write_space();
26162            }
26163            self.generate_expression(error)?;
26164            self.write_keyword(" ON ERROR");
26165        }
26166        if let Some(null) = &e.null {
26167            if e.empty.is_some() || e.error.is_some() {
26168                self.write_space();
26169            }
26170            self.generate_expression(null)?;
26171            self.write_keyword(" ON NULL");
26172        }
26173        Ok(())
26174    }
26175
26176    fn generate_on_conflict(&mut self, e: &OnConflict) -> Result<()> {
26177        // Materialize doesn't support ON CONFLICT - skip entirely
26178        if matches!(self.config.dialect, Some(DialectType::Materialize)) {
26179            return Ok(());
26180        }
26181        // Python: ON CONFLICT|ON DUPLICATE KEY [ON CONSTRAINT constraint] [conflict_keys] action
26182        if e.duplicate.is_some() {
26183            // MySQL: ON DUPLICATE KEY UPDATE col = val, ...
26184            self.write_keyword("ON DUPLICATE KEY UPDATE");
26185            for (i, expr) in e.expressions.iter().enumerate() {
26186                if i > 0 {
26187                    self.write(",");
26188                }
26189                self.write_space();
26190                self.generate_expression(expr)?;
26191            }
26192            return Ok(());
26193        } else {
26194            self.write_keyword("ON CONFLICT");
26195        }
26196        if let Some(constraint) = &e.constraint {
26197            self.write_keyword(" ON CONSTRAINT ");
26198            self.generate_expression(constraint)?;
26199        }
26200        if let Some(conflict_keys) = &e.conflict_keys {
26201            // conflict_keys can be a Tuple containing expressions
26202            if let Expression::Tuple(t) = conflict_keys.as_ref() {
26203                self.write("(");
26204                for (i, expr) in t.expressions.iter().enumerate() {
26205                    if i > 0 {
26206                        self.write(", ");
26207                    }
26208                    self.generate_expression(expr)?;
26209                }
26210                self.write(")");
26211            } else {
26212                self.write("(");
26213                self.generate_expression(conflict_keys)?;
26214                self.write(")");
26215            }
26216        }
26217        if let Some(index_predicate) = &e.index_predicate {
26218            self.write_keyword(" WHERE ");
26219            self.generate_expression(index_predicate)?;
26220        }
26221        if let Some(action) = &e.action {
26222            // Check if action is "NOTHING" or an UPDATE set
26223            if let Expression::Identifier(id) = action.as_ref() {
26224                if id.name == "NOTHING" || id.name.to_uppercase() == "NOTHING" {
26225                    self.write_keyword(" DO NOTHING");
26226                } else {
26227                    self.write_keyword(" DO ");
26228                    self.generate_expression(action)?;
26229                }
26230            } else if let Expression::Tuple(t) = action.as_ref() {
26231                // DO UPDATE SET col1 = val1, col2 = val2
26232                self.write_keyword(" DO UPDATE SET ");
26233                for (i, expr) in t.expressions.iter().enumerate() {
26234                    if i > 0 {
26235                        self.write(", ");
26236                    }
26237                    self.generate_expression(expr)?;
26238                }
26239            } else {
26240                self.write_keyword(" DO ");
26241                self.generate_expression(action)?;
26242            }
26243        }
26244        // WHERE clause for the UPDATE action
26245        if let Some(where_) = &e.where_ {
26246            self.write_keyword(" WHERE ");
26247            self.generate_expression(where_)?;
26248        }
26249        Ok(())
26250    }
26251
26252    fn generate_on_property(&mut self, e: &OnProperty) -> Result<()> {
26253        // ON property_value
26254        self.write_keyword("ON");
26255        self.write_space();
26256        self.generate_expression(&e.this)?;
26257        Ok(())
26258    }
26259
26260    fn generate_opclass(&mut self, e: &Opclass) -> Result<()> {
26261        // Python: this expression (e.g., column opclass)
26262        self.generate_expression(&e.this)?;
26263        self.write_space();
26264        self.generate_expression(&e.expression)?;
26265        Ok(())
26266    }
26267
26268    fn generate_open_json(&mut self, e: &OpenJSON) -> Result<()> {
26269        // Python: OPENJSON(this[, path]) [WITH (columns)]
26270        self.write_keyword("OPENJSON");
26271        self.write("(");
26272        self.generate_expression(&e.this)?;
26273        if let Some(path) = &e.path {
26274            self.write(", ");
26275            self.generate_expression(path)?;
26276        }
26277        self.write(")");
26278        if !e.expressions.is_empty() {
26279            self.write_keyword(" WITH");
26280            if self.config.pretty {
26281                self.write(" (\n");
26282                self.indent_level += 2;
26283                for (i, expr) in e.expressions.iter().enumerate() {
26284                    if i > 0 {
26285                        self.write(",\n");
26286                    }
26287                    self.write_indent();
26288                    self.generate_expression(expr)?;
26289                }
26290                self.write("\n");
26291                self.indent_level -= 2;
26292                self.write(")");
26293            } else {
26294                self.write(" (");
26295                for (i, expr) in e.expressions.iter().enumerate() {
26296                    if i > 0 {
26297                        self.write(", ");
26298                    }
26299                    self.generate_expression(expr)?;
26300                }
26301                self.write(")");
26302            }
26303        }
26304        Ok(())
26305    }
26306
26307    fn generate_open_json_column_def(&mut self, e: &OpenJSONColumnDef) -> Result<()> {
26308        // Python: this kind [path] [AS JSON]
26309        self.generate_expression(&e.this)?;
26310        self.write_space();
26311        // Use parsed data_type if available, otherwise fall back to kind string
26312        if let Some(ref dt) = e.data_type {
26313            self.generate_data_type(dt)?;
26314        } else if !e.kind.is_empty() {
26315            self.write(&e.kind);
26316        }
26317        if let Some(path) = &e.path {
26318            self.write_space();
26319            self.generate_expression(path)?;
26320        }
26321        if e.as_json.is_some() {
26322            self.write_keyword(" AS JSON");
26323        }
26324        Ok(())
26325    }
26326
26327    fn generate_operator(&mut self, e: &Operator) -> Result<()> {
26328        // this OPERATOR(op) expression
26329        self.generate_expression(&e.this)?;
26330        self.write_space();
26331        if let Some(op) = &e.operator {
26332            self.write_keyword("OPERATOR");
26333            self.write("(");
26334            self.generate_expression(op)?;
26335            self.write(")");
26336        }
26337        self.write_space();
26338        self.generate_expression(&e.expression)?;
26339        Ok(())
26340    }
26341
26342    fn generate_order_by(&mut self, e: &OrderBy) -> Result<()> {
26343        // ORDER BY expr1 [ASC|DESC] [NULLS FIRST|LAST], expr2 ...
26344        self.write_keyword("ORDER BY");
26345        let pretty_clickhouse_single_paren = self.config.pretty
26346            && matches!(self.config.dialect, Some(DialectType::ClickHouse))
26347            && e.expressions.len() == 1
26348            && matches!(e.expressions[0].this, Expression::Paren(ref p) if !matches!(p.this, Expression::Tuple(_)));
26349        let clickhouse_single_tuple = matches!(self.config.dialect, Some(DialectType::ClickHouse))
26350            && e.expressions.len() == 1
26351            && matches!(e.expressions[0].this, Expression::Tuple(_))
26352            && !e.expressions[0].desc
26353            && e.expressions[0].nulls_first.is_none();
26354
26355        if pretty_clickhouse_single_paren {
26356            self.write_space();
26357            if let Expression::Paren(p) = &e.expressions[0].this {
26358                self.write("(");
26359                self.write_newline();
26360                self.indent_level += 1;
26361                self.write_indent();
26362                self.generate_expression(&p.this)?;
26363                self.indent_level -= 1;
26364                self.write_newline();
26365                self.write(")");
26366            }
26367            return Ok(());
26368        }
26369
26370        if clickhouse_single_tuple {
26371            self.write_space();
26372            if let Expression::Tuple(t) = &e.expressions[0].this {
26373                self.write("(");
26374                for (i, expr) in t.expressions.iter().enumerate() {
26375                    if i > 0 {
26376                        self.write(", ");
26377                    }
26378                    self.generate_expression(expr)?;
26379                }
26380                self.write(")");
26381            }
26382            return Ok(());
26383        }
26384
26385        self.write_space();
26386        for (i, ordered) in e.expressions.iter().enumerate() {
26387            if i > 0 {
26388                self.write(", ");
26389            }
26390            self.generate_expression(&ordered.this)?;
26391            if ordered.desc {
26392                self.write_space();
26393                self.write_keyword("DESC");
26394            } else if ordered.explicit_asc {
26395                self.write_space();
26396                self.write_keyword("ASC");
26397            }
26398            if let Some(nulls_first) = ordered.nulls_first {
26399                // In Dremio, NULLS LAST is the default, so skip generating it
26400                let skip_nulls_last = !nulls_first
26401                    && matches!(self.config.dialect, Some(DialectType::Dremio));
26402                if !skip_nulls_last {
26403                    self.write_space();
26404                    self.write_keyword("NULLS");
26405                    self.write_space();
26406                    if nulls_first {
26407                        self.write_keyword("FIRST");
26408                    } else {
26409                        self.write_keyword("LAST");
26410                    }
26411                }
26412            }
26413        }
26414        Ok(())
26415    }
26416
26417    fn generate_output_model_property(&mut self, e: &OutputModelProperty) -> Result<()> {
26418        // OUTPUT(model)
26419        self.write_keyword("OUTPUT");
26420        self.write("(");
26421        if self.config.pretty {
26422            self.indent_level += 1;
26423            self.write_newline();
26424            self.write_indent();
26425            self.generate_expression(&e.this)?;
26426            self.indent_level -= 1;
26427            self.write_newline();
26428        } else {
26429            self.generate_expression(&e.this)?;
26430        }
26431        self.write(")");
26432        Ok(())
26433    }
26434
26435    fn generate_overflow_truncate_behavior(&mut self, e: &OverflowTruncateBehavior) -> Result<()> {
26436        // Python: TRUNCATE [filler] WITH|WITHOUT COUNT
26437        self.write_keyword("TRUNCATE");
26438        if let Some(this) = &e.this {
26439            self.write_space();
26440            self.generate_expression(this)?;
26441        }
26442        if e.with_count.is_some() {
26443            self.write_keyword(" WITH COUNT");
26444        } else {
26445            self.write_keyword(" WITHOUT COUNT");
26446        }
26447        Ok(())
26448    }
26449
26450    fn generate_parameterized_agg(&mut self, e: &ParameterizedAgg) -> Result<()> {
26451        // Python: name(expressions)(params)
26452        self.generate_expression(&e.this)?;
26453        self.write("(");
26454        for (i, expr) in e.expressions.iter().enumerate() {
26455            if i > 0 {
26456                self.write(", ");
26457            }
26458            self.generate_expression(expr)?;
26459        }
26460        self.write(")(");
26461        for (i, param) in e.params.iter().enumerate() {
26462            if i > 0 {
26463                self.write(", ");
26464            }
26465            self.generate_expression(param)?;
26466        }
26467        self.write(")");
26468        Ok(())
26469    }
26470
26471    fn generate_parse_datetime(&mut self, e: &ParseDatetime) -> Result<()> {
26472        // PARSE_DATETIME(format, this) or similar
26473        self.write_keyword("PARSE_DATETIME");
26474        self.write("(");
26475        if let Some(format) = &e.format {
26476            self.write("'");
26477            self.write(format);
26478            self.write("', ");
26479        }
26480        self.generate_expression(&e.this)?;
26481        if let Some(zone) = &e.zone {
26482            self.write(", ");
26483            self.generate_expression(zone)?;
26484        }
26485        self.write(")");
26486        Ok(())
26487    }
26488
26489    fn generate_parse_ip(&mut self, e: &ParseIp) -> Result<()> {
26490        // PARSE_IP(this, type, permissive)
26491        self.write_keyword("PARSE_IP");
26492        self.write("(");
26493        self.generate_expression(&e.this)?;
26494        if let Some(type_) = &e.type_ {
26495            self.write(", ");
26496            self.generate_expression(type_)?;
26497        }
26498        if let Some(permissive) = &e.permissive {
26499            self.write(", ");
26500            self.generate_expression(permissive)?;
26501        }
26502        self.write(")");
26503        Ok(())
26504    }
26505
26506    fn generate_parse_json(&mut self, e: &ParseJSON) -> Result<()> {
26507        // PARSE_JSON(this, [expression])
26508        self.write_keyword("PARSE_JSON");
26509        self.write("(");
26510        self.generate_expression(&e.this)?;
26511        if let Some(expression) = &e.expression {
26512            self.write(", ");
26513            self.generate_expression(expression)?;
26514        }
26515        self.write(")");
26516        Ok(())
26517    }
26518
26519    fn generate_parse_time(&mut self, e: &ParseTime) -> Result<()> {
26520        // PARSE_TIME(format, this) or STR_TO_TIME(this, format)
26521        self.write_keyword("PARSE_TIME");
26522        self.write("(");
26523        self.write(&format!("'{}'", e.format));
26524        self.write(", ");
26525        self.generate_expression(&e.this)?;
26526        self.write(")");
26527        Ok(())
26528    }
26529
26530    fn generate_parse_url(&mut self, e: &ParseUrl) -> Result<()> {
26531        // PARSE_URL(this, [part_to_extract], [key], [permissive])
26532        self.write_keyword("PARSE_URL");
26533        self.write("(");
26534        self.generate_expression(&e.this)?;
26535        if let Some(part) = &e.part_to_extract {
26536            self.write(", ");
26537            self.generate_expression(part)?;
26538        }
26539        if let Some(key) = &e.key {
26540            self.write(", ");
26541            self.generate_expression(key)?;
26542        }
26543        if let Some(permissive) = &e.permissive {
26544            self.write(", ");
26545            self.generate_expression(permissive)?;
26546        }
26547        self.write(")");
26548        Ok(())
26549    }
26550
26551    fn generate_partition_expr(&mut self, e: &Partition) -> Result<()> {
26552        // PARTITION(expr1, expr2, ...) or SUBPARTITION(expr1, expr2, ...)
26553        if e.subpartition {
26554            self.write_keyword("SUBPARTITION");
26555        } else {
26556            self.write_keyword("PARTITION");
26557        }
26558        self.write("(");
26559        for (i, expr) in e.expressions.iter().enumerate() {
26560            if i > 0 {
26561                self.write(", ");
26562            }
26563            self.generate_expression(expr)?;
26564        }
26565        self.write(")");
26566        Ok(())
26567    }
26568
26569    fn generate_partition_bound_spec(&mut self, e: &PartitionBoundSpec) -> Result<()> {
26570        // IN (values) or WITH (MODULUS this, REMAINDER expression) or FROM (from) TO (to)
26571        if let Some(this) = &e.this {
26572            if let Some(expression) = &e.expression {
26573                // WITH (MODULUS this, REMAINDER expression)
26574                self.write_keyword("WITH");
26575                self.write(" (");
26576                self.write_keyword("MODULUS");
26577                self.write_space();
26578                self.generate_expression(this)?;
26579                self.write(", ");
26580                self.write_keyword("REMAINDER");
26581                self.write_space();
26582                self.generate_expression(expression)?;
26583                self.write(")");
26584            } else {
26585                // IN (this) - this could be a list
26586                self.write_keyword("IN");
26587                self.write(" (");
26588                self.generate_partition_bound_values(this)?;
26589                self.write(")");
26590            }
26591        } else if let (Some(from), Some(to)) = (&e.from_expressions, &e.to_expressions) {
26592            // FROM (from_expressions) TO (to_expressions)
26593            self.write_keyword("FROM");
26594            self.write(" (");
26595            self.generate_partition_bound_values(from)?;
26596            self.write(") ");
26597            self.write_keyword("TO");
26598            self.write(" (");
26599            self.generate_partition_bound_values(to)?;
26600            self.write(")");
26601        }
26602        Ok(())
26603    }
26604
26605    /// Generate partition bound values - handles Tuple expressions by outputting
26606    /// contents without wrapping parens (since caller provides the parens)
26607    fn generate_partition_bound_values(&mut self, expr: &Expression) -> Result<()> {
26608        if let Expression::Tuple(t) = expr {
26609            for (i, e) in t.expressions.iter().enumerate() {
26610                if i > 0 {
26611                    self.write(", ");
26612                }
26613                self.generate_expression(e)?;
26614            }
26615            Ok(())
26616        } else {
26617            self.generate_expression(expr)
26618        }
26619    }
26620
26621    fn generate_partition_by_list_property(&mut self, e: &PartitionByListProperty) -> Result<()> {
26622        // PARTITION BY LIST (partition_expressions) (create_expressions)
26623        self.write_keyword("PARTITION BY LIST");
26624        if let Some(partition_exprs) = &e.partition_expressions {
26625            self.write(" (");
26626            // Unwrap Tuple for partition columns (don't generate outer parens from Tuple)
26627            self.generate_doris_partition_expressions(partition_exprs)?;
26628            self.write(")");
26629        }
26630        if let Some(create_exprs) = &e.create_expressions {
26631            self.write(" (");
26632            // Unwrap Tuple for partition definitions
26633            self.generate_doris_partition_definitions(create_exprs)?;
26634            self.write(")");
26635        }
26636        Ok(())
26637    }
26638
26639    fn generate_partition_by_range_property(&mut self, e: &PartitionByRangeProperty) -> Result<()> {
26640        // PARTITION BY RANGE (partition_expressions) (create_expressions)
26641        self.write_keyword("PARTITION BY RANGE");
26642        if let Some(partition_exprs) = &e.partition_expressions {
26643            self.write(" (");
26644            // Unwrap Tuple for partition columns (don't generate outer parens from Tuple)
26645            self.generate_doris_partition_expressions(partition_exprs)?;
26646            self.write(")");
26647        }
26648        if let Some(create_exprs) = &e.create_expressions {
26649            self.write(" (");
26650            // Check for dynamic partition (PartitionByRangePropertyDynamic) or static (Tuple of Partition)
26651            self.generate_doris_partition_definitions(create_exprs)?;
26652            self.write(")");
26653        }
26654        Ok(())
26655    }
26656
26657    /// Generate Doris partition column expressions (unwrap Tuple)
26658    fn generate_doris_partition_expressions(&mut self, expr: &Expression) -> Result<()> {
26659        if let Expression::Tuple(t) = expr {
26660            for (i, e) in t.expressions.iter().enumerate() {
26661                if i > 0 {
26662                    self.write(", ");
26663                }
26664                self.generate_expression(e)?;
26665            }
26666        } else {
26667            self.generate_expression(expr)?;
26668        }
26669        Ok(())
26670    }
26671
26672    /// Generate Doris partition definitions (comma-separated Partition expressions)
26673    fn generate_doris_partition_definitions(&mut self, expr: &Expression) -> Result<()> {
26674        match expr {
26675            Expression::Tuple(t) => {
26676                // Multiple partitions, comma-separated
26677                for (i, part) in t.expressions.iter().enumerate() {
26678                    if i > 0 {
26679                        self.write(", ");
26680                    }
26681                    // For Partition expressions, generate the inner PartitionRange/PartitionList directly
26682                    if let Expression::Partition(p) = part {
26683                        for (j, inner) in p.expressions.iter().enumerate() {
26684                            if j > 0 {
26685                                self.write(", ");
26686                            }
26687                            self.generate_expression(inner)?;
26688                        }
26689                    } else {
26690                        self.generate_expression(part)?;
26691                    }
26692                }
26693            }
26694            Expression::PartitionByRangePropertyDynamic(_) => {
26695                // Dynamic partition - FROM/TO/INTERVAL
26696                self.generate_expression(expr)?;
26697            }
26698            _ => {
26699                self.generate_expression(expr)?;
26700            }
26701        }
26702        Ok(())
26703    }
26704
26705    fn generate_partition_by_range_property_dynamic(&mut self, e: &PartitionByRangePropertyDynamic) -> Result<()> {
26706        // Doris: FROM (start) TO (end) INTERVAL n UNIT
26707        if let Some(start) = &e.start {
26708            self.write_keyword("FROM");
26709            self.write(" (");
26710            self.generate_expression(start)?;
26711            self.write(")");
26712        }
26713        if let Some(end) = &e.end {
26714            self.write_space();
26715            self.write_keyword("TO");
26716            self.write(" (");
26717            self.generate_expression(end)?;
26718            self.write(")");
26719        }
26720        if let Some(every) = &e.every {
26721            self.write_space();
26722            // Generate INTERVAL n UNIT (not quoted, for Doris dynamic partition)
26723            self.generate_doris_interval(every)?;
26724        }
26725        Ok(())
26726    }
26727
26728    /// Generate Doris-style interval without quoting numbers: INTERVAL n UNIT
26729    fn generate_doris_interval(&mut self, expr: &Expression) -> Result<()> {
26730        if let Expression::Interval(interval) = expr {
26731            self.write_keyword("INTERVAL");
26732            if let Some(ref value) = interval.this {
26733                self.write_space();
26734                // Don't quote numbers for Doris partition interval
26735                self.generate_expression(value)?;
26736            }
26737            if let Some(ref unit_spec) = interval.unit {
26738                self.write_space();
26739                self.write_interval_unit_spec(unit_spec)?;
26740            }
26741            Ok(())
26742        } else {
26743            self.generate_expression(expr)
26744        }
26745    }
26746
26747    fn generate_partition_by_truncate(&mut self, e: &PartitionByTruncate) -> Result<()> {
26748        // TRUNCATE(expression, this)
26749        self.write_keyword("TRUNCATE");
26750        self.write("(");
26751        self.generate_expression(&e.expression)?;
26752        self.write(", ");
26753        self.generate_expression(&e.this)?;
26754        self.write(")");
26755        Ok(())
26756    }
26757
26758    fn generate_partition_list(&mut self, e: &PartitionList) -> Result<()> {
26759        // Doris: PARTITION name VALUES IN (val1, val2)
26760        self.write_keyword("PARTITION");
26761        self.write_space();
26762        self.generate_expression(&e.this)?;
26763        self.write_space();
26764        self.write_keyword("VALUES IN");
26765        self.write(" (");
26766        for (i, expr) in e.expressions.iter().enumerate() {
26767            if i > 0 {
26768                self.write(", ");
26769            }
26770            self.generate_expression(expr)?;
26771        }
26772        self.write(")");
26773        Ok(())
26774    }
26775
26776    fn generate_partition_range(&mut self, e: &PartitionRange) -> Result<()> {
26777        // Check if this is a TSQL-style simple range (e.g., "2 TO 5")
26778        // TSQL ranges have no expressions and just use `this TO expression`
26779        if e.expressions.is_empty() && e.expression.is_some() {
26780            // TSQL: simple range like "2 TO 5" - no PARTITION keyword
26781            self.generate_expression(&e.this)?;
26782            self.write_space();
26783            self.write_keyword("TO");
26784            self.write_space();
26785            self.generate_expression(e.expression.as_ref().unwrap())?;
26786            return Ok(());
26787        }
26788
26789        // Doris: PARTITION name VALUES LESS THAN (val) or PARTITION name VALUES [(val1), (val2))
26790        self.write_keyword("PARTITION");
26791        self.write_space();
26792        self.generate_expression(&e.this)?;
26793        self.write_space();
26794
26795        // Check if expressions contain Tuple (bracket notation) or single values (LESS THAN)
26796        if e.expressions.len() == 1 {
26797            // Single value: VALUES LESS THAN (val)
26798            self.write_keyword("VALUES LESS THAN");
26799            self.write(" (");
26800            self.generate_expression(&e.expressions[0])?;
26801            self.write(")");
26802        } else if !e.expressions.is_empty() {
26803            // Multiple values with Tuple: VALUES [(val1), (val2))
26804            self.write_keyword("VALUES");
26805            self.write(" [");
26806            for (i, expr) in e.expressions.iter().enumerate() {
26807                if i > 0 {
26808                    self.write(", ");
26809                }
26810                // If the expr is a Tuple, generate its contents wrapped in parens
26811                if let Expression::Tuple(t) = expr {
26812                    self.write("(");
26813                    for (j, inner) in t.expressions.iter().enumerate() {
26814                        if j > 0 {
26815                            self.write(", ");
26816                        }
26817                        self.generate_expression(inner)?;
26818                    }
26819                    self.write(")");
26820                } else {
26821                    self.write("(");
26822                    self.generate_expression(expr)?;
26823                    self.write(")");
26824                }
26825            }
26826            self.write(")");
26827        }
26828        Ok(())
26829    }
26830
26831    fn generate_partitioned_by_bucket(&mut self, e: &PartitionedByBucket) -> Result<()> {
26832        // BUCKET(this, expression)
26833        self.write_keyword("BUCKET");
26834        self.write("(");
26835        self.generate_expression(&e.this)?;
26836        self.write(", ");
26837        self.generate_expression(&e.expression)?;
26838        self.write(")");
26839        Ok(())
26840    }
26841
26842    fn generate_partitioned_by_property(&mut self, e: &PartitionedByProperty) -> Result<()> {
26843        // PARTITIONED BY this (Teradata/ClickHouse use PARTITION BY)
26844        if matches!(
26845            self.config.dialect,
26846            Some(crate::dialects::DialectType::Teradata) | Some(crate::dialects::DialectType::ClickHouse)
26847        ) {
26848            self.write_keyword("PARTITION BY");
26849        } else {
26850            self.write_keyword("PARTITIONED BY");
26851        }
26852        self.write_space();
26853        // In pretty mode, always use multiline tuple format for PARTITIONED BY
26854        if self.config.pretty {
26855            if let Expression::Tuple(ref tuple) = *e.this {
26856                self.write("(");
26857                self.write_newline();
26858                self.indent_level += 1;
26859                for (i, expr) in tuple.expressions.iter().enumerate() {
26860                    if i > 0 {
26861                        self.write(",");
26862                        self.write_newline();
26863                    }
26864                    self.write_indent();
26865                    self.generate_expression(expr)?;
26866                }
26867                self.indent_level -= 1;
26868                self.write_newline();
26869                self.write(")");
26870            } else {
26871                self.generate_expression(&e.this)?;
26872            }
26873        } else {
26874            self.generate_expression(&e.this)?;
26875        }
26876        Ok(())
26877    }
26878
26879    fn generate_partitioned_of_property(&mut self, e: &PartitionedOfProperty) -> Result<()> {
26880        // PARTITION OF this FOR VALUES expression or PARTITION OF this DEFAULT
26881        self.write_keyword("PARTITION OF");
26882        self.write_space();
26883        self.generate_expression(&e.this)?;
26884        // Check if expression is a PartitionBoundSpec
26885        if let Expression::PartitionBoundSpec(_) = e.expression.as_ref() {
26886            self.write_space();
26887            self.write_keyword("FOR VALUES");
26888            self.write_space();
26889            self.generate_expression(&e.expression)?;
26890        } else {
26891            self.write_space();
26892            self.write_keyword("DEFAULT");
26893        }
26894        Ok(())
26895    }
26896
26897    fn generate_period_for_system_time_constraint(&mut self, e: &PeriodForSystemTimeConstraint) -> Result<()> {
26898        // PERIOD FOR SYSTEM_TIME (this, expression)
26899        self.write_keyword("PERIOD FOR SYSTEM_TIME");
26900        self.write(" (");
26901        self.generate_expression(&e.this)?;
26902        self.write(", ");
26903        self.generate_expression(&e.expression)?;
26904        self.write(")");
26905        Ok(())
26906    }
26907
26908    fn generate_pivot_alias(&mut self, e: &PivotAlias) -> Result<()> {
26909        // value AS alias
26910        // The alias can be an identifier or an expression (e.g., string concatenation)
26911        self.generate_expression(&e.this)?;
26912        self.write_space();
26913        self.write_keyword("AS");
26914        self.write_space();
26915        // When target dialect uses identifiers for UNPIVOT aliases, convert literals to identifiers
26916        if self.config.unpivot_aliases_are_identifiers {
26917            match &e.alias {
26918                Expression::Literal(Literal::String(s)) => {
26919                    // Convert string literal to identifier
26920                    self.generate_identifier(&Identifier::new(s.clone()))?;
26921                }
26922                Expression::Literal(Literal::Number(n)) => {
26923                    // Convert number literal to quoted identifier
26924                    let mut id = Identifier::new(n.clone());
26925                    id.quoted = true;
26926                    self.generate_identifier(&id)?;
26927                }
26928                other => {
26929                    self.generate_expression(other)?;
26930                }
26931            }
26932        } else {
26933            self.generate_expression(&e.alias)?;
26934        }
26935        Ok(())
26936    }
26937
26938    fn generate_pivot_any(&mut self, e: &PivotAny) -> Result<()> {
26939        // ANY or ANY [expression]
26940        self.write_keyword("ANY");
26941        if let Some(this) = &e.this {
26942            self.write_space();
26943            self.generate_expression(this)?;
26944        }
26945        Ok(())
26946    }
26947
26948    fn generate_predict(&mut self, e: &Predict) -> Result<()> {
26949        // ML.PREDICT(MODEL this, expression, [params_struct])
26950        self.write_keyword("ML.PREDICT");
26951        self.write("(");
26952        self.write_keyword("MODEL");
26953        self.write_space();
26954        self.generate_expression(&e.this)?;
26955        self.write(", ");
26956        self.generate_expression(&e.expression)?;
26957        if let Some(params) = &e.params_struct {
26958            self.write(", ");
26959            self.generate_expression(params)?;
26960        }
26961        self.write(")");
26962        Ok(())
26963    }
26964
26965    fn generate_previous_day(&mut self, e: &PreviousDay) -> Result<()> {
26966        // PREVIOUS_DAY(this, expression)
26967        self.write_keyword("PREVIOUS_DAY");
26968        self.write("(");
26969        self.generate_expression(&e.this)?;
26970        self.write(", ");
26971        self.generate_expression(&e.expression)?;
26972        self.write(")");
26973        Ok(())
26974    }
26975
26976    fn generate_primary_key(&mut self, e: &PrimaryKey) -> Result<()> {
26977        // PRIMARY KEY [name] (columns) [INCLUDE (...)] [options]
26978        self.write_keyword("PRIMARY KEY");
26979        if let Some(name) = &e.this {
26980            self.write_space();
26981            self.generate_expression(name)?;
26982        }
26983        if !e.expressions.is_empty() {
26984            self.write(" (");
26985            for (i, expr) in e.expressions.iter().enumerate() {
26986                if i > 0 {
26987                    self.write(", ");
26988                }
26989                self.generate_expression(expr)?;
26990            }
26991            self.write(")");
26992        }
26993        if let Some(include) = &e.include {
26994            self.write_space();
26995            self.generate_expression(include)?;
26996        }
26997        if !e.options.is_empty() {
26998            self.write_space();
26999            for (i, opt) in e.options.iter().enumerate() {
27000                if i > 0 {
27001                    self.write_space();
27002                }
27003                self.generate_expression(opt)?;
27004            }
27005        }
27006        Ok(())
27007    }
27008
27009    fn generate_primary_key_column_constraint(&mut self, _e: &PrimaryKeyColumnConstraint) -> Result<()> {
27010        // PRIMARY KEY constraint at column level
27011        self.write_keyword("PRIMARY KEY");
27012        Ok(())
27013    }
27014
27015    fn generate_path_column_constraint(&mut self, e: &PathColumnConstraint) -> Result<()> {
27016        // PATH 'xpath' constraint for XMLTABLE/JSON_TABLE columns
27017        self.write_keyword("PATH");
27018        self.write_space();
27019        self.generate_expression(&e.this)?;
27020        Ok(())
27021    }
27022
27023    fn generate_projection_def(&mut self, e: &ProjectionDef) -> Result<()> {
27024        // PROJECTION this (expression)
27025        self.write_keyword("PROJECTION");
27026        self.write_space();
27027        self.generate_expression(&e.this)?;
27028        self.write(" (");
27029        self.generate_expression(&e.expression)?;
27030        self.write(")");
27031        Ok(())
27032    }
27033
27034    fn generate_properties(&mut self, e: &Properties) -> Result<()> {
27035        // Properties list
27036        for (i, prop) in e.expressions.iter().enumerate() {
27037            if i > 0 {
27038                self.write(", ");
27039            }
27040            self.generate_expression(prop)?;
27041        }
27042        Ok(())
27043    }
27044
27045    fn generate_property(&mut self, e: &Property) -> Result<()> {
27046        // name=value
27047        self.generate_expression(&e.this)?;
27048        if let Some(value) = &e.value {
27049            self.write("=");
27050            self.generate_expression(value)?;
27051        }
27052        Ok(())
27053    }
27054
27055    /// Generate BigQuery-style OPTIONS clause: OPTIONS (key=value, key=value, ...)
27056    fn generate_options_clause(&mut self, options: &[Expression]) -> Result<()> {
27057        self.write_keyword("OPTIONS");
27058        self.write(" (");
27059        for (i, opt) in options.iter().enumerate() {
27060            if i > 0 {
27061                self.write(", ");
27062            }
27063            self.generate_option_expression(opt)?;
27064        }
27065        self.write(")");
27066        Ok(())
27067    }
27068
27069    /// Generate Doris/StarRocks-style PROPERTIES clause: PROPERTIES ('key'='value', 'key'='value', ...)
27070    fn generate_properties_clause(&mut self, properties: &[Expression]) -> Result<()> {
27071        self.write_keyword("PROPERTIES");
27072        self.write(" (");
27073        for (i, prop) in properties.iter().enumerate() {
27074            if i > 0 {
27075                self.write(", ");
27076            }
27077            self.generate_option_expression(prop)?;
27078        }
27079        self.write(")");
27080        Ok(())
27081    }
27082
27083    /// Generate Databricks-style ENVIRONMENT clause: ENVIRONMENT (key = 'value', key = 'value', ...)
27084    fn generate_environment_clause(&mut self, environment: &[Expression]) -> Result<()> {
27085        self.write_keyword("ENVIRONMENT");
27086        self.write(" (");
27087        for (i, env_item) in environment.iter().enumerate() {
27088            if i > 0 {
27089                self.write(", ");
27090            }
27091            self.generate_environment_expression(env_item)?;
27092        }
27093        self.write(")");
27094        Ok(())
27095    }
27096
27097    /// Generate an environment expression with spaces around =
27098    fn generate_environment_expression(&mut self, expr: &Expression) -> Result<()> {
27099        match expr {
27100            Expression::Eq(eq) => {
27101                // Generate key = value with spaces (Databricks ENVIRONMENT style)
27102                self.generate_expression(&eq.left)?;
27103                self.write(" = ");
27104                self.generate_expression(&eq.right)?;
27105                Ok(())
27106            }
27107            _ => self.generate_expression(expr),
27108        }
27109    }
27110
27111    /// Generate Hive-style TBLPROPERTIES clause: TBLPROPERTIES ('key'='value', ...)
27112    fn generate_tblproperties_clause(&mut self, options: &[Expression]) -> Result<()> {
27113        self.write_keyword("TBLPROPERTIES");
27114        if self.config.pretty {
27115            self.write(" (");
27116            self.write_newline();
27117            self.indent_level += 1;
27118            for (i, opt) in options.iter().enumerate() {
27119                if i > 0 {
27120                    self.write(",");
27121                    self.write_newline();
27122                }
27123                self.write_indent();
27124                self.generate_option_expression(opt)?;
27125            }
27126            self.indent_level -= 1;
27127            self.write_newline();
27128            self.write(")");
27129        } else {
27130            self.write(" (");
27131            for (i, opt) in options.iter().enumerate() {
27132                if i > 0 {
27133                    self.write(", ");
27134                }
27135                self.generate_option_expression(opt)?;
27136            }
27137            self.write(")");
27138        }
27139        Ok(())
27140    }
27141
27142    /// Generate an option expression without spaces around =
27143    fn generate_option_expression(&mut self, expr: &Expression) -> Result<()> {
27144        match expr {
27145            Expression::Eq(eq) => {
27146                // Generate key=value without spaces
27147                self.generate_expression(&eq.left)?;
27148                self.write("=");
27149                self.generate_expression(&eq.right)?;
27150                Ok(())
27151            }
27152            _ => self.generate_expression(expr),
27153        }
27154    }
27155
27156    fn generate_pseudo_type(&mut self, e: &PseudoType) -> Result<()> {
27157        // Just output the name
27158        self.generate_expression(&e.this)?;
27159        Ok(())
27160    }
27161
27162    fn generate_put(&mut self, e: &PutStmt) -> Result<()> {
27163        // PUT source_file @stage [options]
27164        self.write_keyword("PUT");
27165        self.write_space();
27166
27167        // Source file path - preserve original quoting
27168        if e.source_quoted {
27169            self.write("'");
27170            self.write(&e.source);
27171            self.write("'");
27172        } else {
27173            self.write(&e.source);
27174        }
27175
27176        self.write_space();
27177
27178        // Target stage reference - output the string directly (includes @)
27179        if let Expression::Literal(Literal::String(s)) = &e.target {
27180            self.write(s);
27181        } else {
27182            self.generate_expression(&e.target)?;
27183        }
27184
27185        // Optional parameters: KEY=VALUE
27186        for param in &e.params {
27187            self.write_space();
27188            self.write(&param.name);
27189            if let Some(ref value) = param.value {
27190                self.write("=");
27191                self.generate_expression(value)?;
27192            }
27193        }
27194
27195        Ok(())
27196    }
27197
27198    fn generate_quantile(&mut self, e: &Quantile) -> Result<()> {
27199        // QUANTILE(this, quantile)
27200        self.write_keyword("QUANTILE");
27201        self.write("(");
27202        self.generate_expression(&e.this)?;
27203        if let Some(quantile) = &e.quantile {
27204            self.write(", ");
27205            self.generate_expression(quantile)?;
27206        }
27207        self.write(")");
27208        Ok(())
27209    }
27210
27211    fn generate_query_band(&mut self, e: &QueryBand) -> Result<()> {
27212        // QUERY_BAND = this [UPDATE] [FOR scope]
27213        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Teradata)) {
27214            self.write_keyword("SET");
27215            self.write_space();
27216        }
27217        self.write_keyword("QUERY_BAND");
27218        self.write(" = ");
27219        self.generate_expression(&e.this)?;
27220        if e.update.is_some() {
27221            self.write_space();
27222            self.write_keyword("UPDATE");
27223        }
27224        if let Some(scope) = &e.scope {
27225            self.write_space();
27226            self.write_keyword("FOR");
27227            self.write_space();
27228            self.generate_expression(scope)?;
27229        }
27230        Ok(())
27231    }
27232
27233    fn generate_query_option(&mut self, e: &QueryOption) -> Result<()> {
27234        // this = expression
27235        self.generate_expression(&e.this)?;
27236        if let Some(expression) = &e.expression {
27237            self.write(" = ");
27238            self.generate_expression(expression)?;
27239        }
27240        Ok(())
27241    }
27242
27243    fn generate_query_transform(&mut self, e: &QueryTransform) -> Result<()> {
27244        // TRANSFORM (expressions) [row_format_before] [RECORDWRITER record_writer] USING command_script [AS schema] [row_format_after] [RECORDREADER record_reader]
27245        self.write_keyword("TRANSFORM");
27246        self.write("(");
27247        for (i, expr) in e.expressions.iter().enumerate() {
27248            if i > 0 {
27249                self.write(", ");
27250            }
27251            self.generate_expression(expr)?;
27252        }
27253        self.write(")");
27254        if let Some(row_format_before) = &e.row_format_before {
27255            self.write_space();
27256            self.generate_expression(row_format_before)?;
27257        }
27258        if let Some(record_writer) = &e.record_writer {
27259            self.write_space();
27260            self.write_keyword("RECORDWRITER");
27261            self.write_space();
27262            self.generate_expression(record_writer)?;
27263        }
27264        if let Some(command_script) = &e.command_script {
27265            self.write_space();
27266            self.write_keyword("USING");
27267            self.write_space();
27268            self.generate_expression(command_script)?;
27269        }
27270        if let Some(schema) = &e.schema {
27271            self.write_space();
27272            self.write_keyword("AS");
27273            self.write_space();
27274            self.generate_expression(schema)?;
27275        }
27276        if let Some(row_format_after) = &e.row_format_after {
27277            self.write_space();
27278            self.generate_expression(row_format_after)?;
27279        }
27280        if let Some(record_reader) = &e.record_reader {
27281            self.write_space();
27282            self.write_keyword("RECORDREADER");
27283            self.write_space();
27284            self.generate_expression(record_reader)?;
27285        }
27286        Ok(())
27287    }
27288
27289    fn generate_randn(&mut self, e: &Randn) -> Result<()> {
27290        // RANDN([seed])
27291        self.write_keyword("RANDN");
27292        self.write("(");
27293        if let Some(this) = &e.this {
27294            self.generate_expression(this)?;
27295        }
27296        self.write(")");
27297        Ok(())
27298    }
27299
27300    fn generate_randstr(&mut self, e: &Randstr) -> Result<()> {
27301        // RANDSTR(this, [generator])
27302        self.write_keyword("RANDSTR");
27303        self.write("(");
27304        self.generate_expression(&e.this)?;
27305        if let Some(generator) = &e.generator {
27306            self.write(", ");
27307            self.generate_expression(generator)?;
27308        }
27309        self.write(")");
27310        Ok(())
27311    }
27312
27313    fn generate_range_bucket(&mut self, e: &RangeBucket) -> Result<()> {
27314        // RANGE_BUCKET(this, expression)
27315        self.write_keyword("RANGE_BUCKET");
27316        self.write("(");
27317        self.generate_expression(&e.this)?;
27318        self.write(", ");
27319        self.generate_expression(&e.expression)?;
27320        self.write(")");
27321        Ok(())
27322    }
27323
27324    fn generate_range_n(&mut self, e: &RangeN) -> Result<()> {
27325        // RANGE_N(this BETWEEN expressions [EACH each])
27326        self.write_keyword("RANGE_N");
27327        self.write("(");
27328        self.generate_expression(&e.this)?;
27329        self.write_space();
27330        self.write_keyword("BETWEEN");
27331        self.write_space();
27332        for (i, expr) in e.expressions.iter().enumerate() {
27333            if i > 0 {
27334                self.write(", ");
27335            }
27336            self.generate_expression(expr)?;
27337        }
27338        if let Some(each) = &e.each {
27339            self.write_space();
27340            self.write_keyword("EACH");
27341            self.write_space();
27342            self.generate_expression(each)?;
27343        }
27344        self.write(")");
27345        Ok(())
27346    }
27347
27348    fn generate_read_csv(&mut self, e: &ReadCSV) -> Result<()> {
27349        // READ_CSV(this, expressions...)
27350        self.write_keyword("READ_CSV");
27351        self.write("(");
27352        self.generate_expression(&e.this)?;
27353        for expr in &e.expressions {
27354            self.write(", ");
27355            self.generate_expression(expr)?;
27356        }
27357        self.write(")");
27358        Ok(())
27359    }
27360
27361    fn generate_read_parquet(&mut self, e: &ReadParquet) -> Result<()> {
27362        // READ_PARQUET(expressions...)
27363        self.write_keyword("READ_PARQUET");
27364        self.write("(");
27365        for (i, expr) in e.expressions.iter().enumerate() {
27366            if i > 0 {
27367                self.write(", ");
27368            }
27369            self.generate_expression(expr)?;
27370        }
27371        self.write(")");
27372        Ok(())
27373    }
27374
27375    fn generate_recursive_with_search(&mut self, e: &RecursiveWithSearch) -> Result<()> {
27376        // SEARCH kind FIRST BY this SET expression [USING using]
27377        // or CYCLE this SET expression [USING using]
27378        if e.kind == "CYCLE" {
27379            self.write_keyword("CYCLE");
27380        } else {
27381            self.write_keyword("SEARCH");
27382            self.write_space();
27383            self.write(&e.kind);
27384            self.write_space();
27385            self.write_keyword("FIRST BY");
27386        }
27387        self.write_space();
27388        self.generate_expression(&e.this)?;
27389        self.write_space();
27390        self.write_keyword("SET");
27391        self.write_space();
27392        self.generate_expression(&e.expression)?;
27393        if let Some(using) = &e.using {
27394            self.write_space();
27395            self.write_keyword("USING");
27396            self.write_space();
27397            self.generate_expression(using)?;
27398        }
27399        Ok(())
27400    }
27401
27402    fn generate_reduce(&mut self, e: &Reduce) -> Result<()> {
27403        // REDUCE(this, initial, merge, [finish])
27404        self.write_keyword("REDUCE");
27405        self.write("(");
27406        self.generate_expression(&e.this)?;
27407        if let Some(initial) = &e.initial {
27408            self.write(", ");
27409            self.generate_expression(initial)?;
27410        }
27411        if let Some(merge) = &e.merge {
27412            self.write(", ");
27413            self.generate_expression(merge)?;
27414        }
27415        if let Some(finish) = &e.finish {
27416            self.write(", ");
27417            self.generate_expression(finish)?;
27418        }
27419        self.write(")");
27420        Ok(())
27421    }
27422
27423    fn generate_reference(&mut self, e: &Reference) -> Result<()> {
27424        // REFERENCES this (expressions) [options]
27425        self.write_keyword("REFERENCES");
27426        self.write_space();
27427        self.generate_expression(&e.this)?;
27428        if !e.expressions.is_empty() {
27429            self.write(" (");
27430            for (i, expr) in e.expressions.iter().enumerate() {
27431                if i > 0 {
27432                    self.write(", ");
27433                }
27434                self.generate_expression(expr)?;
27435            }
27436            self.write(")");
27437        }
27438        for opt in &e.options {
27439            self.write_space();
27440            self.generate_expression(opt)?;
27441        }
27442        Ok(())
27443    }
27444
27445    fn generate_refresh(&mut self, e: &Refresh) -> Result<()> {
27446        // REFRESH [kind] this
27447        self.write_keyword("REFRESH");
27448        if !e.kind.is_empty() {
27449            self.write_space();
27450            self.write_keyword(&e.kind);
27451        }
27452        self.write_space();
27453        self.generate_expression(&e.this)?;
27454        Ok(())
27455    }
27456
27457    fn generate_refresh_trigger_property(&mut self, e: &RefreshTriggerProperty) -> Result<()> {
27458        // Doris REFRESH clause: REFRESH method ON kind [EVERY n UNIT] [STARTS 'datetime']
27459        self.write_keyword("REFRESH");
27460        self.write_space();
27461        self.write_keyword(&e.method);
27462
27463        if let Some(ref kind) = e.kind {
27464            self.write_space();
27465            self.write_keyword("ON");
27466            self.write_space();
27467            self.write_keyword(kind);
27468
27469            // EVERY n UNIT
27470            if let Some(ref every) = e.every {
27471                self.write_space();
27472                self.write_keyword("EVERY");
27473                self.write_space();
27474                self.generate_expression(every)?;
27475                if let Some(ref unit) = e.unit {
27476                    self.write_space();
27477                    self.write_keyword(unit);
27478                }
27479            }
27480
27481            // STARTS 'datetime'
27482            if let Some(ref starts) = e.starts {
27483                self.write_space();
27484                self.write_keyword("STARTS");
27485                self.write_space();
27486                self.generate_expression(starts)?;
27487            }
27488        }
27489        Ok(())
27490    }
27491
27492    fn generate_regexp_count(&mut self, e: &RegexpCount) -> Result<()> {
27493        // REGEXP_COUNT(this, expression, position, parameters)
27494        self.write_keyword("REGEXP_COUNT");
27495        self.write("(");
27496        self.generate_expression(&e.this)?;
27497        self.write(", ");
27498        self.generate_expression(&e.expression)?;
27499        if let Some(position) = &e.position {
27500            self.write(", ");
27501            self.generate_expression(position)?;
27502        }
27503        if let Some(parameters) = &e.parameters {
27504            self.write(", ");
27505            self.generate_expression(parameters)?;
27506        }
27507        self.write(")");
27508        Ok(())
27509    }
27510
27511    fn generate_regexp_extract_all(&mut self, e: &RegexpExtractAll) -> Result<()> {
27512        // REGEXP_EXTRACT_ALL(this, expression, group, parameters, position, occurrence)
27513        self.write_keyword("REGEXP_EXTRACT_ALL");
27514        self.write("(");
27515        self.generate_expression(&e.this)?;
27516        self.write(", ");
27517        self.generate_expression(&e.expression)?;
27518        if let Some(group) = &e.group {
27519            self.write(", ");
27520            self.generate_expression(group)?;
27521        }
27522        self.write(")");
27523        Ok(())
27524    }
27525
27526    fn generate_regexp_full_match(&mut self, e: &RegexpFullMatch) -> Result<()> {
27527        // REGEXP_FULL_MATCH(this, expression)
27528        self.write_keyword("REGEXP_FULL_MATCH");
27529        self.write("(");
27530        self.generate_expression(&e.this)?;
27531        self.write(", ");
27532        self.generate_expression(&e.expression)?;
27533        self.write(")");
27534        Ok(())
27535    }
27536
27537    fn generate_regexp_i_like(&mut self, e: &RegexpILike) -> Result<()> {
27538        use crate::dialects::DialectType;
27539        // PostgreSQL/Redshift uses ~* operator for case-insensitive regex matching
27540        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) && e.flag.is_none() {
27541            self.generate_expression(&e.this)?;
27542            self.write(" ~* ");
27543            self.generate_expression(&e.expression)?;
27544        } else if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
27545            // Snowflake uses REGEXP_LIKE(x, pattern, 'i')
27546            self.write_keyword("REGEXP_LIKE");
27547            self.write("(");
27548            self.generate_expression(&e.this)?;
27549            self.write(", ");
27550            self.generate_expression(&e.expression)?;
27551            self.write(", ");
27552            if let Some(flag) = &e.flag {
27553                self.generate_expression(flag)?;
27554            } else {
27555                self.write("'i'");
27556            }
27557            self.write(")");
27558        } else {
27559            // this REGEXP_ILIKE expression or REGEXP_ILIKE(this, expression, flag)
27560            self.generate_expression(&e.this)?;
27561            self.write_space();
27562            self.write_keyword("REGEXP_ILIKE");
27563            self.write_space();
27564            self.generate_expression(&e.expression)?;
27565            if let Some(flag) = &e.flag {
27566                self.write(", ");
27567                self.generate_expression(flag)?;
27568            }
27569        }
27570        Ok(())
27571    }
27572
27573    fn generate_regexp_instr(&mut self, e: &RegexpInstr) -> Result<()> {
27574        // REGEXP_INSTR(this, expression, position, occurrence, option, parameters, group)
27575        self.write_keyword("REGEXP_INSTR");
27576        self.write("(");
27577        self.generate_expression(&e.this)?;
27578        self.write(", ");
27579        self.generate_expression(&e.expression)?;
27580        if let Some(position) = &e.position {
27581            self.write(", ");
27582            self.generate_expression(position)?;
27583        }
27584        if let Some(occurrence) = &e.occurrence {
27585            self.write(", ");
27586            self.generate_expression(occurrence)?;
27587        }
27588        if let Some(option) = &e.option {
27589            self.write(", ");
27590            self.generate_expression(option)?;
27591        }
27592        if let Some(parameters) = &e.parameters {
27593            self.write(", ");
27594            self.generate_expression(parameters)?;
27595        }
27596        if let Some(group) = &e.group {
27597            self.write(", ");
27598            self.generate_expression(group)?;
27599        }
27600        self.write(")");
27601        Ok(())
27602    }
27603
27604    fn generate_regexp_split(&mut self, e: &RegexpSplit) -> Result<()> {
27605        // REGEXP_SPLIT(this, expression, limit)
27606        self.write_keyword("REGEXP_SPLIT");
27607        self.write("(");
27608        self.generate_expression(&e.this)?;
27609        self.write(", ");
27610        self.generate_expression(&e.expression)?;
27611        if let Some(limit) = &e.limit {
27612            self.write(", ");
27613            self.generate_expression(limit)?;
27614        }
27615        self.write(")");
27616        Ok(())
27617    }
27618
27619    fn generate_regr_avgx(&mut self, e: &RegrAvgx) -> Result<()> {
27620        // REGR_AVGX(this, expression)
27621        self.write_keyword("REGR_AVGX");
27622        self.write("(");
27623        self.generate_expression(&e.this)?;
27624        self.write(", ");
27625        self.generate_expression(&e.expression)?;
27626        self.write(")");
27627        Ok(())
27628    }
27629
27630    fn generate_regr_avgy(&mut self, e: &RegrAvgy) -> Result<()> {
27631        // REGR_AVGY(this, expression)
27632        self.write_keyword("REGR_AVGY");
27633        self.write("(");
27634        self.generate_expression(&e.this)?;
27635        self.write(", ");
27636        self.generate_expression(&e.expression)?;
27637        self.write(")");
27638        Ok(())
27639    }
27640
27641    fn generate_regr_count(&mut self, e: &RegrCount) -> Result<()> {
27642        // REGR_COUNT(this, expression)
27643        self.write_keyword("REGR_COUNT");
27644        self.write("(");
27645        self.generate_expression(&e.this)?;
27646        self.write(", ");
27647        self.generate_expression(&e.expression)?;
27648        self.write(")");
27649        Ok(())
27650    }
27651
27652    fn generate_regr_intercept(&mut self, e: &RegrIntercept) -> Result<()> {
27653        // REGR_INTERCEPT(this, expression)
27654        self.write_keyword("REGR_INTERCEPT");
27655        self.write("(");
27656        self.generate_expression(&e.this)?;
27657        self.write(", ");
27658        self.generate_expression(&e.expression)?;
27659        self.write(")");
27660        Ok(())
27661    }
27662
27663    fn generate_regr_r2(&mut self, e: &RegrR2) -> Result<()> {
27664        // REGR_R2(this, expression)
27665        self.write_keyword("REGR_R2");
27666        self.write("(");
27667        self.generate_expression(&e.this)?;
27668        self.write(", ");
27669        self.generate_expression(&e.expression)?;
27670        self.write(")");
27671        Ok(())
27672    }
27673
27674    fn generate_regr_slope(&mut self, e: &RegrSlope) -> Result<()> {
27675        // REGR_SLOPE(this, expression)
27676        self.write_keyword("REGR_SLOPE");
27677        self.write("(");
27678        self.generate_expression(&e.this)?;
27679        self.write(", ");
27680        self.generate_expression(&e.expression)?;
27681        self.write(")");
27682        Ok(())
27683    }
27684
27685    fn generate_regr_sxx(&mut self, e: &RegrSxx) -> Result<()> {
27686        // REGR_SXX(this, expression)
27687        self.write_keyword("REGR_SXX");
27688        self.write("(");
27689        self.generate_expression(&e.this)?;
27690        self.write(", ");
27691        self.generate_expression(&e.expression)?;
27692        self.write(")");
27693        Ok(())
27694    }
27695
27696    fn generate_regr_sxy(&mut self, e: &RegrSxy) -> Result<()> {
27697        // REGR_SXY(this, expression)
27698        self.write_keyword("REGR_SXY");
27699        self.write("(");
27700        self.generate_expression(&e.this)?;
27701        self.write(", ");
27702        self.generate_expression(&e.expression)?;
27703        self.write(")");
27704        Ok(())
27705    }
27706
27707    fn generate_regr_syy(&mut self, e: &RegrSyy) -> Result<()> {
27708        // REGR_SYY(this, expression)
27709        self.write_keyword("REGR_SYY");
27710        self.write("(");
27711        self.generate_expression(&e.this)?;
27712        self.write(", ");
27713        self.generate_expression(&e.expression)?;
27714        self.write(")");
27715        Ok(())
27716    }
27717
27718    fn generate_regr_valx(&mut self, e: &RegrValx) -> Result<()> {
27719        // REGR_VALX(this, expression)
27720        self.write_keyword("REGR_VALX");
27721        self.write("(");
27722        self.generate_expression(&e.this)?;
27723        self.write(", ");
27724        self.generate_expression(&e.expression)?;
27725        self.write(")");
27726        Ok(())
27727    }
27728
27729    fn generate_regr_valy(&mut self, e: &RegrValy) -> Result<()> {
27730        // REGR_VALY(this, expression)
27731        self.write_keyword("REGR_VALY");
27732        self.write("(");
27733        self.generate_expression(&e.this)?;
27734        self.write(", ");
27735        self.generate_expression(&e.expression)?;
27736        self.write(")");
27737        Ok(())
27738    }
27739
27740    fn generate_remote_with_connection_model_property(&mut self, e: &RemoteWithConnectionModelProperty) -> Result<()> {
27741        // REMOTE WITH CONNECTION this
27742        self.write_keyword("REMOTE WITH CONNECTION");
27743        self.write_space();
27744        self.generate_expression(&e.this)?;
27745        Ok(())
27746    }
27747
27748    fn generate_rename_column(&mut self, e: &RenameColumn) -> Result<()> {
27749        // RENAME COLUMN [IF EXISTS] this TO new_name
27750        self.write_keyword("RENAME COLUMN");
27751        if e.exists {
27752            self.write_space();
27753            self.write_keyword("IF EXISTS");
27754        }
27755        self.write_space();
27756        self.generate_expression(&e.this)?;
27757        if let Some(to) = &e.to {
27758            self.write_space();
27759            self.write_keyword("TO");
27760            self.write_space();
27761            self.generate_expression(to)?;
27762        }
27763        Ok(())
27764    }
27765
27766    fn generate_replace_partition(&mut self, e: &ReplacePartition) -> Result<()> {
27767        // REPLACE PARTITION expression [FROM source]
27768        self.write_keyword("REPLACE PARTITION");
27769        self.write_space();
27770        self.generate_expression(&e.expression)?;
27771        if let Some(source) = &e.source {
27772            self.write_space();
27773            self.write_keyword("FROM");
27774            self.write_space();
27775            self.generate_expression(source)?;
27776        }
27777        Ok(())
27778    }
27779
27780    fn generate_returning(&mut self, e: &Returning) -> Result<()> {
27781        // RETURNING expressions [INTO into]
27782        // TSQL and Fabric use OUTPUT instead of RETURNING
27783        let keyword = match self.config.dialect {
27784            Some(DialectType::TSQL) | Some(DialectType::Fabric) => "OUTPUT",
27785            _ => "RETURNING",
27786        };
27787        self.write_keyword(keyword);
27788        self.write_space();
27789        for (i, expr) in e.expressions.iter().enumerate() {
27790            if i > 0 {
27791                self.write(", ");
27792            }
27793            self.generate_expression(expr)?;
27794        }
27795        if let Some(into) = &e.into {
27796            self.write_space();
27797            self.write_keyword("INTO");
27798            self.write_space();
27799            self.generate_expression(into)?;
27800        }
27801        Ok(())
27802    }
27803
27804    fn generate_output_clause(&mut self, output: &OutputClause) -> Result<()> {
27805        // OUTPUT expressions [INTO into_table]
27806        self.write_space();
27807        self.write_keyword("OUTPUT");
27808        self.write_space();
27809        for (i, expr) in output.columns.iter().enumerate() {
27810            if i > 0 {
27811                self.write(", ");
27812            }
27813            self.generate_expression(expr)?;
27814        }
27815        if let Some(into_table) = &output.into_table {
27816            self.write_space();
27817            self.write_keyword("INTO");
27818            self.write_space();
27819            self.generate_expression(into_table)?;
27820        }
27821        Ok(())
27822    }
27823
27824    fn generate_returns_property(&mut self, e: &ReturnsProperty) -> Result<()> {
27825        // RETURNS [TABLE] this [NULL ON NULL INPUT | CALLED ON NULL INPUT]
27826        self.write_keyword("RETURNS");
27827        if e.is_table.is_some() {
27828            self.write_space();
27829            self.write_keyword("TABLE");
27830        }
27831        if let Some(table) = &e.table {
27832            self.write_space();
27833            self.generate_expression(table)?;
27834        } else if let Some(this) = &e.this {
27835            self.write_space();
27836            self.generate_expression(this)?;
27837        }
27838        if e.null.is_some() {
27839            self.write_space();
27840            self.write_keyword("NULL ON NULL INPUT");
27841        }
27842        Ok(())
27843    }
27844
27845    fn generate_rollback(&mut self, e: &Rollback) -> Result<()> {
27846        // ROLLBACK [TRANSACTION [transaction_name]] [TO savepoint]
27847        self.write_keyword("ROLLBACK");
27848
27849        // TSQL always uses ROLLBACK TRANSACTION
27850        if e.this.is_none() && matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
27851            self.write_space();
27852            self.write_keyword("TRANSACTION");
27853        }
27854
27855        // Check if this has TRANSACTION keyword or transaction name
27856        if let Some(this) = &e.this {
27857            // Check if it's just the "TRANSACTION" marker or an actual transaction name
27858            let is_transaction_marker = matches!(
27859                this.as_ref(),
27860                Expression::Identifier(id) if id.name == "TRANSACTION"
27861            );
27862
27863            self.write_space();
27864            self.write_keyword("TRANSACTION");
27865
27866            // If it's a real transaction name, output it
27867            if !is_transaction_marker {
27868                self.write_space();
27869                self.generate_expression(this)?;
27870            }
27871        }
27872
27873        // Output TO savepoint
27874        if let Some(savepoint) = &e.savepoint {
27875            self.write_space();
27876            self.write_keyword("TO");
27877            self.write_space();
27878            self.generate_expression(savepoint)?;
27879        }
27880        Ok(())
27881    }
27882
27883    fn generate_rollup(&mut self, e: &Rollup) -> Result<()> {
27884        // Python: return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
27885        if e.expressions.is_empty() {
27886            self.write_keyword("WITH ROLLUP");
27887        } else {
27888            self.write_keyword("ROLLUP");
27889            self.write("(");
27890            for (i, expr) in e.expressions.iter().enumerate() {
27891                if i > 0 {
27892                    self.write(", ");
27893                }
27894                self.generate_expression(expr)?;
27895            }
27896            self.write(")");
27897        }
27898        Ok(())
27899    }
27900
27901    fn generate_row_format_delimited_property(&mut self, e: &RowFormatDelimitedProperty) -> Result<()> {
27902        // ROW FORMAT DELIMITED [FIELDS TERMINATED BY ...] [ESCAPED BY ...] [COLLECTION ITEMS TERMINATED BY ...] [MAP KEYS TERMINATED BY ...] [LINES TERMINATED BY ...] [NULL DEFINED AS ...]
27903        self.write_keyword("ROW FORMAT DELIMITED");
27904        if let Some(fields) = &e.fields {
27905            self.write_space();
27906            self.write_keyword("FIELDS TERMINATED BY");
27907            self.write_space();
27908            self.generate_expression(fields)?;
27909        }
27910        if let Some(escaped) = &e.escaped {
27911            self.write_space();
27912            self.write_keyword("ESCAPED BY");
27913            self.write_space();
27914            self.generate_expression(escaped)?;
27915        }
27916        if let Some(items) = &e.collection_items {
27917            self.write_space();
27918            self.write_keyword("COLLECTION ITEMS TERMINATED BY");
27919            self.write_space();
27920            self.generate_expression(items)?;
27921        }
27922        if let Some(keys) = &e.map_keys {
27923            self.write_space();
27924            self.write_keyword("MAP KEYS TERMINATED BY");
27925            self.write_space();
27926            self.generate_expression(keys)?;
27927        }
27928        if let Some(lines) = &e.lines {
27929            self.write_space();
27930            self.write_keyword("LINES TERMINATED BY");
27931            self.write_space();
27932            self.generate_expression(lines)?;
27933        }
27934        if let Some(null) = &e.null {
27935            self.write_space();
27936            self.write_keyword("NULL DEFINED AS");
27937            self.write_space();
27938            self.generate_expression(null)?;
27939        }
27940        if let Some(serde) = &e.serde {
27941            self.write_space();
27942            self.generate_expression(serde)?;
27943        }
27944        Ok(())
27945    }
27946
27947    fn generate_row_format_property(&mut self, e: &RowFormatProperty) -> Result<()> {
27948        // ROW FORMAT this
27949        self.write_keyword("ROW FORMAT");
27950        self.write_space();
27951        self.generate_expression(&e.this)?;
27952        Ok(())
27953    }
27954
27955    fn generate_row_format_serde_property(&mut self, e: &RowFormatSerdeProperty) -> Result<()> {
27956        // ROW FORMAT SERDE this [WITH SERDEPROPERTIES (...)]
27957        self.write_keyword("ROW FORMAT SERDE");
27958        self.write_space();
27959        self.generate_expression(&e.this)?;
27960        if let Some(props) = &e.serde_properties {
27961            self.write_space();
27962            // SerdeProperties generates its own "[WITH] SERDEPROPERTIES (...)"
27963            self.generate_expression(props)?;
27964        }
27965        Ok(())
27966    }
27967
27968    fn generate_sha2(&mut self, e: &SHA2) -> Result<()> {
27969        // SHA2(this, length)
27970        self.write_keyword("SHA2");
27971        self.write("(");
27972        self.generate_expression(&e.this)?;
27973        if let Some(length) = e.length {
27974            self.write(", ");
27975            self.write(&length.to_string());
27976        }
27977        self.write(")");
27978        Ok(())
27979    }
27980
27981    fn generate_sha2_digest(&mut self, e: &SHA2Digest) -> Result<()> {
27982        // SHA2_DIGEST(this, length)
27983        self.write_keyword("SHA2_DIGEST");
27984        self.write("(");
27985        self.generate_expression(&e.this)?;
27986        if let Some(length) = e.length {
27987            self.write(", ");
27988            self.write(&length.to_string());
27989        }
27990        self.write(")");
27991        Ok(())
27992    }
27993
27994    fn generate_safe_add(&mut self, e: &SafeAdd) -> Result<()> {
27995        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
27996            "TRY_ADD"
27997        } else {
27998            "SAFE_ADD"
27999        };
28000        self.write_keyword(name);
28001        self.write("(");
28002        self.generate_expression(&e.this)?;
28003        self.write(", ");
28004        self.generate_expression(&e.expression)?;
28005        self.write(")");
28006        Ok(())
28007    }
28008
28009    fn generate_safe_divide(&mut self, e: &SafeDivide) -> Result<()> {
28010        // SAFE_DIVIDE(this, expression)
28011        self.write_keyword("SAFE_DIVIDE");
28012        self.write("(");
28013        self.generate_expression(&e.this)?;
28014        self.write(", ");
28015        self.generate_expression(&e.expression)?;
28016        self.write(")");
28017        Ok(())
28018    }
28019
28020    fn generate_safe_multiply(&mut self, e: &SafeMultiply) -> Result<()> {
28021        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
28022            "TRY_MULTIPLY"
28023        } else {
28024            "SAFE_MULTIPLY"
28025        };
28026        self.write_keyword(name);
28027        self.write("(");
28028        self.generate_expression(&e.this)?;
28029        self.write(", ");
28030        self.generate_expression(&e.expression)?;
28031        self.write(")");
28032        Ok(())
28033    }
28034
28035    fn generate_safe_subtract(&mut self, e: &SafeSubtract) -> Result<()> {
28036        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
28037            "TRY_SUBTRACT"
28038        } else {
28039            "SAFE_SUBTRACT"
28040        };
28041        self.write_keyword(name);
28042        self.write("(");
28043        self.generate_expression(&e.this)?;
28044        self.write(", ");
28045        self.generate_expression(&e.expression)?;
28046        self.write(")");
28047        Ok(())
28048    }
28049
28050    /// Generate the body of a USING SAMPLE or TABLESAMPLE clause:
28051    /// METHOD (size UNIT) [REPEATABLE (seed)]
28052    fn generate_sample_body(&mut self, sample: &Sample) -> Result<()> {
28053        // Handle BUCKET sampling: TABLESAMPLE (BUCKET n OUT OF m [ON col])
28054        if matches!(sample.method, SampleMethod::Bucket) {
28055            self.write(" (");
28056            self.write_keyword("BUCKET");
28057            self.write_space();
28058            if let Some(ref num) = sample.bucket_numerator {
28059                self.generate_expression(num)?;
28060            }
28061            self.write_space();
28062            self.write_keyword("OUT OF");
28063            self.write_space();
28064            if let Some(ref denom) = sample.bucket_denominator {
28065                self.generate_expression(denom)?;
28066            }
28067            if let Some(ref field) = sample.bucket_field {
28068                self.write_space();
28069                self.write_keyword("ON");
28070                self.write_space();
28071                self.generate_expression(field)?;
28072            }
28073            self.write(")");
28074            return Ok(());
28075        }
28076
28077        // Output method name if explicitly specified, or for dialects that always require it
28078        let is_snowflake = matches!(self.config.dialect, Some(crate::dialects::DialectType::Snowflake));
28079        let is_postgres = matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL) | Some(crate::dialects::DialectType::Redshift));
28080        // Databricks and Spark don't output method names
28081        let is_databricks = matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks));
28082        let is_spark = matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark));
28083        let suppress_method = is_databricks || is_spark || sample.suppress_method_output;
28084        // PostgreSQL always outputs BERNOULLI for BERNOULLI samples
28085        let force_method = is_postgres && matches!(sample.method, SampleMethod::Bernoulli);
28086        if !suppress_method && (sample.explicit_method || is_snowflake || force_method) {
28087            self.write_space();
28088            if !sample.explicit_method && (is_snowflake || force_method) {
28089                // Snowflake/PostgreSQL defaults to BERNOULLI when no method is specified
28090                self.write_keyword("BERNOULLI");
28091            } else {
28092                match sample.method {
28093                    SampleMethod::Bernoulli => self.write_keyword("BERNOULLI"),
28094                    SampleMethod::System => self.write_keyword("SYSTEM"),
28095                    SampleMethod::Block => self.write_keyword("BLOCK"),
28096                    SampleMethod::Row => self.write_keyword("ROW"),
28097                    SampleMethod::Reservoir => self.write_keyword("RESERVOIR"),
28098                    SampleMethod::Percent => self.write_keyword("SYSTEM"),
28099                    SampleMethod::Bucket => {} // handled above
28100                }
28101            }
28102        }
28103
28104        // Output size, with or without parentheses depending on dialect
28105        let emit_size_no_parens = !self.config.tablesample_requires_parens;
28106        if emit_size_no_parens {
28107            self.write_space();
28108            match &sample.size {
28109                Expression::Tuple(tuple) => {
28110                    for (i, expr) in tuple.expressions.iter().enumerate() {
28111                        if i > 0 {
28112                            self.write(", ");
28113                        }
28114                        self.generate_expression(expr)?;
28115                    }
28116                }
28117                expr => self.generate_expression(expr)?,
28118            }
28119        } else {
28120            self.write(" (");
28121            self.generate_expression(&sample.size)?;
28122        }
28123
28124        // Determine unit
28125        let is_rows_method = matches!(sample.method, SampleMethod::Reservoir | SampleMethod::Row | SampleMethod::Bucket);
28126        let is_percent = matches!(sample.method, SampleMethod::Percent | SampleMethod::System | SampleMethod::Bernoulli | SampleMethod::Block);
28127
28128        // For Snowflake, PostgreSQL, and Presto/Trino, only output ROWS/PERCENT when the user explicitly wrote it (unit_after_size).
28129        // These dialects use bare numbers for percentage by default in TABLESAMPLE METHOD(size) syntax.
28130        // For Databricks and Spark, always output PERCENT for percentage samples.
28131        let is_presto = matches!(self.config.dialect, Some(crate::dialects::DialectType::Presto) | Some(crate::dialects::DialectType::Trino) | Some(crate::dialects::DialectType::Athena));
28132        let should_output_unit = if is_databricks || is_spark {
28133            // Always output PERCENT for percentage-based methods, or ROWS for row-based methods
28134            is_percent || is_rows_method || sample.unit_after_size
28135        } else if is_snowflake || is_postgres || is_presto {
28136            sample.unit_after_size
28137        } else {
28138            sample.unit_after_size || (sample.explicit_method && (is_rows_method || is_percent))
28139        };
28140
28141        if should_output_unit {
28142            self.write_space();
28143            if sample.is_percent {
28144                self.write_keyword("PERCENT");
28145            } else if is_rows_method && !sample.unit_after_size {
28146                self.write_keyword("ROWS");
28147            } else if sample.unit_after_size {
28148                match sample.method {
28149                    SampleMethod::Percent | SampleMethod::System | SampleMethod::Bernoulli | SampleMethod::Block => {
28150                        self.write_keyword("PERCENT");
28151                    }
28152                    SampleMethod::Row | SampleMethod::Reservoir => {
28153                        self.write_keyword("ROWS");
28154                    }
28155                    _ => self.write_keyword("ROWS"),
28156                }
28157            } else {
28158                self.write_keyword("PERCENT");
28159            }
28160        }
28161
28162        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28163            if let Some(ref offset) = sample.offset {
28164                self.write_space();
28165                self.write_keyword("OFFSET");
28166                self.write_space();
28167                self.generate_expression(offset)?;
28168            }
28169        }
28170        if !emit_size_no_parens {
28171            self.write(")");
28172        }
28173
28174        Ok(())
28175    }
28176
28177    fn generate_sample_property(&mut self, e: &SampleProperty) -> Result<()> {
28178        // SAMPLE this (ClickHouse uses SAMPLE BY)
28179        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28180            self.write_keyword("SAMPLE BY");
28181        } else {
28182            self.write_keyword("SAMPLE");
28183        }
28184        self.write_space();
28185        self.generate_expression(&e.this)?;
28186        Ok(())
28187    }
28188
28189    fn generate_schema(&mut self, e: &Schema) -> Result<()> {
28190        // this (expressions...)
28191        if let Some(this) = &e.this {
28192            self.generate_expression(this)?;
28193        }
28194        if !e.expressions.is_empty() {
28195            // Add space before column list if there's a preceding expression
28196            if e.this.is_some() {
28197                self.write_space();
28198            }
28199            self.write("(");
28200            for (i, expr) in e.expressions.iter().enumerate() {
28201                if i > 0 {
28202                    self.write(", ");
28203                }
28204                self.generate_expression(expr)?;
28205            }
28206            self.write(")");
28207        }
28208        Ok(())
28209    }
28210
28211    fn generate_schema_comment_property(&mut self, e: &SchemaCommentProperty) -> Result<()> {
28212        // COMMENT this
28213        self.write_keyword("COMMENT");
28214        self.write_space();
28215        self.generate_expression(&e.this)?;
28216        Ok(())
28217    }
28218
28219    fn generate_scope_resolution(&mut self, e: &ScopeResolution) -> Result<()> {
28220        // [this::]expression
28221        if let Some(this) = &e.this {
28222            self.generate_expression(this)?;
28223            self.write("::");
28224        }
28225        self.generate_expression(&e.expression)?;
28226        Ok(())
28227    }
28228
28229    fn generate_search(&mut self, e: &Search) -> Result<()> {
28230        // SEARCH(this, expression, [json_scope], [analyzer], [analyzer_options], [search_mode])
28231        self.write_keyword("SEARCH");
28232        self.write("(");
28233        self.generate_expression(&e.this)?;
28234        self.write(", ");
28235        self.generate_expression(&e.expression)?;
28236        if let Some(json_scope) = &e.json_scope {
28237            self.write(", ");
28238            self.generate_expression(json_scope)?;
28239        }
28240        if let Some(analyzer) = &e.analyzer {
28241            self.write(", ");
28242            self.generate_expression(analyzer)?;
28243        }
28244        if let Some(analyzer_options) = &e.analyzer_options {
28245            self.write(", ");
28246            self.generate_expression(analyzer_options)?;
28247        }
28248        if let Some(search_mode) = &e.search_mode {
28249            self.write(", ");
28250            self.generate_expression(search_mode)?;
28251        }
28252        self.write(")");
28253        Ok(())
28254    }
28255
28256    fn generate_search_ip(&mut self, e: &SearchIp) -> Result<()> {
28257        // SEARCH_IP(this, expression)
28258        self.write_keyword("SEARCH_IP");
28259        self.write("(");
28260        self.generate_expression(&e.this)?;
28261        self.write(", ");
28262        self.generate_expression(&e.expression)?;
28263        self.write(")");
28264        Ok(())
28265    }
28266
28267    fn generate_security_property(&mut self, e: &SecurityProperty) -> Result<()> {
28268        // SECURITY this
28269        self.write_keyword("SECURITY");
28270        self.write_space();
28271        self.generate_expression(&e.this)?;
28272        Ok(())
28273    }
28274
28275    fn generate_semantic_view(&mut self, e: &SemanticView) -> Result<()> {
28276        // SEMANTIC_VIEW(this [METRICS ...] [DIMENSIONS ...] [FACTS ...] [WHERE ...])
28277        self.write("SEMANTIC_VIEW(");
28278
28279        if self.config.pretty {
28280            // Pretty print: each clause on its own line
28281            self.write_newline();
28282            self.indent_level += 1;
28283            self.write_indent();
28284            self.generate_expression(&e.this)?;
28285
28286            if let Some(metrics) = &e.metrics {
28287                self.write_newline();
28288                self.write_indent();
28289                self.write_keyword("METRICS");
28290                self.write_space();
28291                self.generate_semantic_view_tuple(metrics)?;
28292            }
28293            if let Some(dimensions) = &e.dimensions {
28294                self.write_newline();
28295                self.write_indent();
28296                self.write_keyword("DIMENSIONS");
28297                self.write_space();
28298                self.generate_semantic_view_tuple(dimensions)?;
28299            }
28300            if let Some(facts) = &e.facts {
28301                self.write_newline();
28302                self.write_indent();
28303                self.write_keyword("FACTS");
28304                self.write_space();
28305                self.generate_semantic_view_tuple(facts)?;
28306            }
28307            if let Some(where_) = &e.where_ {
28308                self.write_newline();
28309                self.write_indent();
28310                self.write_keyword("WHERE");
28311                self.write_space();
28312                self.generate_expression(where_)?;
28313            }
28314            self.write_newline();
28315            self.indent_level -= 1;
28316            self.write_indent();
28317        } else {
28318            // Compact: all on one line
28319            self.generate_expression(&e.this)?;
28320            if let Some(metrics) = &e.metrics {
28321                self.write_space();
28322                self.write_keyword("METRICS");
28323                self.write_space();
28324                self.generate_semantic_view_tuple(metrics)?;
28325            }
28326            if let Some(dimensions) = &e.dimensions {
28327                self.write_space();
28328                self.write_keyword("DIMENSIONS");
28329                self.write_space();
28330                self.generate_semantic_view_tuple(dimensions)?;
28331            }
28332            if let Some(facts) = &e.facts {
28333                self.write_space();
28334                self.write_keyword("FACTS");
28335                self.write_space();
28336                self.generate_semantic_view_tuple(facts)?;
28337            }
28338            if let Some(where_) = &e.where_ {
28339                self.write_space();
28340                self.write_keyword("WHERE");
28341                self.write_space();
28342                self.generate_expression(where_)?;
28343            }
28344        }
28345        self.write(")");
28346        Ok(())
28347    }
28348
28349    /// Helper for SEMANTIC_VIEW tuple contents (without parentheses)
28350    fn generate_semantic_view_tuple(&mut self, expr: &Expression) -> Result<()> {
28351        if let Expression::Tuple(t) = expr {
28352            for (i, e) in t.expressions.iter().enumerate() {
28353                if i > 0 {
28354                    self.write(", ");
28355                }
28356                self.generate_expression(e)?;
28357            }
28358        } else {
28359            self.generate_expression(expr)?;
28360        }
28361        Ok(())
28362    }
28363
28364    fn generate_sequence_properties(&mut self, e: &SequenceProperties) -> Result<()> {
28365        // [START WITH start] [INCREMENT BY increment] [MINVALUE minvalue] [MAXVALUE maxvalue] [CACHE cache] [OWNED BY owned]
28366        if let Some(start) = &e.start {
28367            self.write_keyword("START WITH");
28368            self.write_space();
28369            self.generate_expression(start)?;
28370        }
28371        if let Some(increment) = &e.increment {
28372            self.write_space();
28373            self.write_keyword("INCREMENT BY");
28374            self.write_space();
28375            self.generate_expression(increment)?;
28376        }
28377        if let Some(minvalue) = &e.minvalue {
28378            self.write_space();
28379            self.write_keyword("MINVALUE");
28380            self.write_space();
28381            self.generate_expression(minvalue)?;
28382        }
28383        if let Some(maxvalue) = &e.maxvalue {
28384            self.write_space();
28385            self.write_keyword("MAXVALUE");
28386            self.write_space();
28387            self.generate_expression(maxvalue)?;
28388        }
28389        if let Some(cache) = &e.cache {
28390            self.write_space();
28391            self.write_keyword("CACHE");
28392            self.write_space();
28393            self.generate_expression(cache)?;
28394        }
28395        if let Some(owned) = &e.owned {
28396            self.write_space();
28397            self.write_keyword("OWNED BY");
28398            self.write_space();
28399            self.generate_expression(owned)?;
28400        }
28401        for opt in &e.options {
28402            self.write_space();
28403            self.generate_expression(opt)?;
28404        }
28405        Ok(())
28406    }
28407
28408    fn generate_serde_properties(&mut self, e: &SerdeProperties) -> Result<()> {
28409        // [WITH] SERDEPROPERTIES (expressions)
28410        if e.with_.is_some() {
28411            self.write_keyword("WITH");
28412            self.write_space();
28413        }
28414        self.write_keyword("SERDEPROPERTIES");
28415        self.write(" (");
28416        for (i, expr) in e.expressions.iter().enumerate() {
28417            if i > 0 {
28418                self.write(", ");
28419            }
28420            // Generate key=value without spaces around =
28421            match expr {
28422                Expression::Eq(eq) => {
28423                    self.generate_expression(&eq.left)?;
28424                    self.write("=");
28425                    self.generate_expression(&eq.right)?;
28426                }
28427                _ => self.generate_expression(expr)?,
28428            }
28429        }
28430        self.write(")");
28431        Ok(())
28432    }
28433
28434    fn generate_session_parameter(&mut self, e: &SessionParameter) -> Result<()> {
28435        // @@[kind.]this
28436        self.write("@@");
28437        if let Some(kind) = &e.kind {
28438            self.write(kind);
28439            self.write(".");
28440        }
28441        self.generate_expression(&e.this)?;
28442        Ok(())
28443    }
28444
28445    fn generate_set(&mut self, e: &Set) -> Result<()> {
28446        // SET/UNSET [TAG] expressions
28447        if e.unset.is_some() {
28448            self.write_keyword("UNSET");
28449        } else {
28450            self.write_keyword("SET");
28451        }
28452        if e.tag.is_some() {
28453            self.write_space();
28454            self.write_keyword("TAG");
28455        }
28456        if !e.expressions.is_empty() {
28457            self.write_space();
28458            for (i, expr) in e.expressions.iter().enumerate() {
28459                if i > 0 {
28460                    self.write(", ");
28461                }
28462                self.generate_expression(expr)?;
28463            }
28464        }
28465        Ok(())
28466    }
28467
28468    fn generate_set_config_property(&mut self, e: &SetConfigProperty) -> Result<()> {
28469        // SET this or SETCONFIG this
28470        self.write_keyword("SET");
28471        self.write_space();
28472        self.generate_expression(&e.this)?;
28473        Ok(())
28474    }
28475
28476    fn generate_set_item(&mut self, e: &SetItem) -> Result<()> {
28477        // [kind] name = value
28478        if let Some(kind) = &e.kind {
28479            self.write_keyword(kind);
28480            self.write_space();
28481        }
28482        self.generate_expression(&e.name)?;
28483        self.write(" = ");
28484        self.generate_expression(&e.value)?;
28485        Ok(())
28486    }
28487
28488    fn generate_set_operation(&mut self, e: &SetOperation) -> Result<()> {
28489        // [WITH ...] this UNION|INTERSECT|EXCEPT [ALL|DISTINCT] [BY NAME] expression
28490        if let Some(with_) = &e.with_ {
28491            self.generate_expression(with_)?;
28492            self.write_space();
28493        }
28494        self.generate_expression(&e.this)?;
28495        self.write_space();
28496        // kind should be UNION, INTERSECT, EXCEPT, etc.
28497        if let Some(kind) = &e.kind {
28498            self.write_keyword(kind);
28499        }
28500        if e.distinct {
28501            self.write_space();
28502            self.write_keyword("DISTINCT");
28503        } else {
28504            self.write_space();
28505            self.write_keyword("ALL");
28506        }
28507        if e.by_name.is_some() {
28508            self.write_space();
28509            self.write_keyword("BY NAME");
28510        }
28511        self.write_space();
28512        self.generate_expression(&e.expression)?;
28513        Ok(())
28514    }
28515
28516    fn generate_set_property(&mut self, e: &SetProperty) -> Result<()> {
28517        // SET or MULTISET
28518        if e.multi.is_some() {
28519            self.write_keyword("MULTISET");
28520        } else {
28521            self.write_keyword("SET");
28522        }
28523        Ok(())
28524    }
28525
28526    fn generate_settings_property(&mut self, e: &SettingsProperty) -> Result<()> {
28527        // SETTINGS expressions
28528        self.write_keyword("SETTINGS");
28529        if self.config.pretty && e.expressions.len() > 1 {
28530            // Pretty print: each setting on its own line, indented
28531            self.indent_level += 1;
28532            for (i, expr) in e.expressions.iter().enumerate() {
28533                if i > 0 {
28534                    self.write(",");
28535                }
28536                self.write_newline();
28537                self.write_indent();
28538                self.generate_expression(expr)?;
28539            }
28540            self.indent_level -= 1;
28541        } else {
28542            self.write_space();
28543            for (i, expr) in e.expressions.iter().enumerate() {
28544                if i > 0 {
28545                    self.write(", ");
28546                }
28547                self.generate_expression(expr)?;
28548            }
28549        }
28550        Ok(())
28551    }
28552
28553    fn generate_sharing_property(&mut self, e: &SharingProperty) -> Result<()> {
28554        // SHARING = this
28555        self.write_keyword("SHARING");
28556        if let Some(this) = &e.this {
28557            self.write(" = ");
28558            self.generate_expression(this)?;
28559        }
28560        Ok(())
28561    }
28562
28563    fn generate_slice(&mut self, e: &Slice) -> Result<()> {
28564        // Python array slicing: begin:end:step
28565        if let Some(begin) = &e.this {
28566            self.generate_expression(begin)?;
28567        }
28568        self.write(":");
28569        if let Some(end) = &e.expression {
28570            self.generate_expression(end)?;
28571        }
28572        if let Some(step) = &e.step {
28573            self.write(":");
28574            self.generate_expression(step)?;
28575        }
28576        Ok(())
28577    }
28578
28579    fn generate_sort_array(&mut self, e: &SortArray) -> Result<()> {
28580        // SORT_ARRAY(this, asc)
28581        self.write_keyword("SORT_ARRAY");
28582        self.write("(");
28583        self.generate_expression(&e.this)?;
28584        if let Some(asc) = &e.asc {
28585            self.write(", ");
28586            self.generate_expression(asc)?;
28587        }
28588        self.write(")");
28589        Ok(())
28590    }
28591
28592    fn generate_sort_by(&mut self, e: &SortBy) -> Result<()> {
28593        // SORT BY expressions
28594        self.write_keyword("SORT BY");
28595        self.write_space();
28596        for (i, expr) in e.expressions.iter().enumerate() {
28597            if i > 0 {
28598                self.write(", ");
28599            }
28600            self.generate_ordered(expr)?;
28601        }
28602        Ok(())
28603    }
28604
28605    fn generate_sort_key_property(&mut self, e: &SortKeyProperty) -> Result<()> {
28606        // [COMPOUND] SORTKEY(col1, col2, ...) - no space before paren
28607        if e.compound.is_some() {
28608            self.write_keyword("COMPOUND");
28609            self.write_space();
28610        }
28611        self.write_keyword("SORTKEY");
28612        self.write("(");
28613        // If this is a Tuple, unwrap its contents to avoid double parentheses
28614        if let Expression::Tuple(t) = e.this.as_ref() {
28615            for (i, expr) in t.expressions.iter().enumerate() {
28616                if i > 0 {
28617                    self.write(", ");
28618                }
28619                self.generate_expression(expr)?;
28620            }
28621        } else {
28622            self.generate_expression(&e.this)?;
28623        }
28624        self.write(")");
28625        Ok(())
28626    }
28627
28628    fn generate_split_part(&mut self, e: &SplitPart) -> Result<()> {
28629        // SPLIT_PART(this, delimiter, part_index)
28630        self.write_keyword("SPLIT_PART");
28631        self.write("(");
28632        self.generate_expression(&e.this)?;
28633        if let Some(delimiter) = &e.delimiter {
28634            self.write(", ");
28635            self.generate_expression(delimiter)?;
28636        }
28637        if let Some(part_index) = &e.part_index {
28638            self.write(", ");
28639            self.generate_expression(part_index)?;
28640        }
28641        self.write(")");
28642        Ok(())
28643    }
28644
28645    fn generate_sql_read_write_property(&mut self, e: &SqlReadWriteProperty) -> Result<()> {
28646        // READS SQL DATA or MODIFIES SQL DATA, etc.
28647        self.generate_expression(&e.this)?;
28648        Ok(())
28649    }
28650
28651    fn generate_sql_security_property(&mut self, e: &SqlSecurityProperty) -> Result<()> {
28652        // SQL SECURITY DEFINER or SQL SECURITY INVOKER
28653        self.write_keyword("SQL SECURITY");
28654        self.write_space();
28655        self.generate_expression(&e.this)?;
28656        Ok(())
28657    }
28658
28659    fn generate_st_distance(&mut self, e: &StDistance) -> Result<()> {
28660        // ST_DISTANCE(this, expression, [use_spheroid])
28661        self.write_keyword("ST_DISTANCE");
28662        self.write("(");
28663        self.generate_expression(&e.this)?;
28664        self.write(", ");
28665        self.generate_expression(&e.expression)?;
28666        if let Some(use_spheroid) = &e.use_spheroid {
28667            self.write(", ");
28668            self.generate_expression(use_spheroid)?;
28669        }
28670        self.write(")");
28671        Ok(())
28672    }
28673
28674    fn generate_st_point(&mut self, e: &StPoint) -> Result<()> {
28675        // ST_POINT(this, expression)
28676        self.write_keyword("ST_POINT");
28677        self.write("(");
28678        self.generate_expression(&e.this)?;
28679        self.write(", ");
28680        self.generate_expression(&e.expression)?;
28681        self.write(")");
28682        Ok(())
28683    }
28684
28685    fn generate_stability_property(&mut self, e: &StabilityProperty) -> Result<()> {
28686        // IMMUTABLE, STABLE, VOLATILE
28687        self.generate_expression(&e.this)?;
28688        Ok(())
28689    }
28690
28691    fn generate_standard_hash(&mut self, e: &StandardHash) -> Result<()> {
28692        // STANDARD_HASH(this, [expression])
28693        self.write_keyword("STANDARD_HASH");
28694        self.write("(");
28695        self.generate_expression(&e.this)?;
28696        if let Some(expression) = &e.expression {
28697            self.write(", ");
28698            self.generate_expression(expression)?;
28699        }
28700        self.write(")");
28701        Ok(())
28702    }
28703
28704    fn generate_storage_handler_property(&mut self, e: &StorageHandlerProperty) -> Result<()> {
28705        // STORED BY this
28706        self.write_keyword("STORED BY");
28707        self.write_space();
28708        self.generate_expression(&e.this)?;
28709        Ok(())
28710    }
28711
28712    fn generate_str_position(&mut self, e: &StrPosition) -> Result<()> {
28713        // STRPOS(this, substr) or STRPOS(this, substr, position)
28714        // Different dialects have different function names
28715        use crate::dialects::DialectType;
28716        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
28717            // Snowflake: CHARINDEX(substr, str[, position])
28718            self.write_keyword("CHARINDEX");
28719            self.write("(");
28720            if let Some(substr) = &e.substr {
28721                self.generate_expression(substr)?;
28722                self.write(", ");
28723            }
28724            self.generate_expression(&e.this)?;
28725            if let Some(position) = &e.position {
28726                self.write(", ");
28727                self.generate_expression(position)?;
28728            }
28729            self.write(")");
28730        } else if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28731            self.write_keyword("POSITION");
28732            self.write("(");
28733            self.generate_expression(&e.this)?;
28734            if let Some(substr) = &e.substr {
28735                self.write(", ");
28736                self.generate_expression(substr)?;
28737            }
28738            if let Some(position) = &e.position {
28739                self.write(", ");
28740                self.generate_expression(position)?;
28741            }
28742            if let Some(occurrence) = &e.occurrence {
28743                self.write(", ");
28744                self.generate_expression(occurrence)?;
28745            }
28746            self.write(")");
28747        } else if matches!(self.config.dialect, Some(DialectType::SQLite) | Some(DialectType::Oracle) | Some(DialectType::BigQuery) | Some(DialectType::Teradata)) {
28748            self.write_keyword("INSTR");
28749            self.write("(");
28750            self.generate_expression(&e.this)?;
28751            if let Some(substr) = &e.substr {
28752                self.write(", ");
28753                self.generate_expression(substr)?;
28754            }
28755            if let Some(position) = &e.position {
28756                self.write(", ");
28757                self.generate_expression(position)?;
28758            } else if e.occurrence.is_some() {
28759                // INSTR requires a position arg before occurrence: INSTR(str, substr, start, nth)
28760                // Default start position is 1
28761                self.write(", 1");
28762            }
28763            if let Some(occurrence) = &e.occurrence {
28764                self.write(", ");
28765                self.generate_expression(occurrence)?;
28766            }
28767            self.write(")");
28768        } else if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::Doris) | Some(DialectType::StarRocks)
28769            | Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)) {
28770            // LOCATE(substr, str[, position]) - substr first
28771            self.write_keyword("LOCATE");
28772            self.write("(");
28773            if let Some(substr) = &e.substr {
28774                self.generate_expression(substr)?;
28775                self.write(", ");
28776            }
28777            self.generate_expression(&e.this)?;
28778            if let Some(position) = &e.position {
28779                self.write(", ");
28780                self.generate_expression(position)?;
28781            }
28782            self.write(")");
28783        } else if matches!(self.config.dialect, Some(DialectType::TSQL)) {
28784            // CHARINDEX(substr, str[, position])
28785            self.write_keyword("CHARINDEX");
28786            self.write("(");
28787            if let Some(substr) = &e.substr {
28788                self.generate_expression(substr)?;
28789                self.write(", ");
28790            }
28791            self.generate_expression(&e.this)?;
28792            if let Some(position) = &e.position {
28793                self.write(", ");
28794                self.generate_expression(position)?;
28795            }
28796            self.write(")");
28797        } else {
28798            self.write_keyword("STRPOS");
28799            self.write("(");
28800            self.generate_expression(&e.this)?;
28801            if let Some(substr) = &e.substr {
28802                self.write(", ");
28803                self.generate_expression(substr)?;
28804            }
28805            if let Some(position) = &e.position {
28806                self.write(", ");
28807                self.generate_expression(position)?;
28808            }
28809            if let Some(occurrence) = &e.occurrence {
28810                self.write(", ");
28811                self.generate_expression(occurrence)?;
28812            }
28813            self.write(")");
28814        }
28815        Ok(())
28816    }
28817
28818    fn generate_str_to_date(&mut self, e: &StrToDate) -> Result<()> {
28819        match self.config.dialect {
28820            Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => {
28821                // TO_DATE(this, java_format)
28822                self.write_keyword("TO_DATE");
28823                self.write("(");
28824                self.generate_expression(&e.this)?;
28825                if let Some(format) = &e.format {
28826                    self.write(", '");
28827                    self.write(&Self::strftime_to_java_format(format));
28828                    self.write("'");
28829                }
28830                self.write(")");
28831            }
28832            Some(DialectType::DuckDB) => {
28833                // CAST(STRPTIME(this, format) AS DATE)
28834                self.write_keyword("CAST");
28835                self.write("(");
28836                self.write_keyword("STRPTIME");
28837                self.write("(");
28838                self.generate_expression(&e.this)?;
28839                if let Some(format) = &e.format {
28840                    self.write(", '");
28841                    self.write(format);
28842                    self.write("'");
28843                }
28844                self.write(")");
28845                self.write_keyword(" AS ");
28846                self.write_keyword("DATE");
28847                self.write(")");
28848            }
28849            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
28850                // TO_DATE(this, pg_format)
28851                self.write_keyword("TO_DATE");
28852                self.write("(");
28853                self.generate_expression(&e.this)?;
28854                if let Some(format) = &e.format {
28855                    self.write(", '");
28856                    self.write(&Self::strftime_to_postgres_format(format));
28857                    self.write("'");
28858                }
28859                self.write(")");
28860            }
28861            Some(DialectType::BigQuery) => {
28862                // PARSE_DATE(format, this) - note: format comes first for BigQuery
28863                self.write_keyword("PARSE_DATE");
28864                self.write("(");
28865                if let Some(format) = &e.format {
28866                    self.write("'");
28867                    self.write(format);
28868                    self.write("'");
28869                    self.write(", ");
28870                }
28871                self.generate_expression(&e.this)?;
28872                self.write(")");
28873            }
28874            Some(DialectType::Teradata) => {
28875                // CAST(this AS DATE FORMAT 'teradata_fmt')
28876                self.write_keyword("CAST");
28877                self.write("(");
28878                self.generate_expression(&e.this)?;
28879                self.write_keyword(" AS ");
28880                self.write_keyword("DATE");
28881                if let Some(format) = &e.format {
28882                    self.write_keyword(" FORMAT ");
28883                    self.write("'");
28884                    self.write(&Self::strftime_to_teradata_format(format));
28885                    self.write("'");
28886                }
28887                self.write(")");
28888            }
28889            _ => {
28890                // STR_TO_DATE(this, format) - MySQL default
28891                self.write_keyword("STR_TO_DATE");
28892                self.write("(");
28893                self.generate_expression(&e.this)?;
28894                if let Some(format) = &e.format {
28895                    self.write(", '");
28896                    self.write(format);
28897                    self.write("'");
28898                }
28899                self.write(")");
28900            }
28901        }
28902        Ok(())
28903    }
28904
28905    /// Convert strftime format to Teradata date format (YYYY, DD, MM, etc.)
28906    fn strftime_to_teradata_format(fmt: &str) -> String {
28907        let mut result = fmt.to_string();
28908        result = result.replace("%Y", "YYYY");
28909        result = result.replace("%y", "YY");
28910        result = result.replace("%m", "MM");
28911        result = result.replace("%B", "MMMM");
28912        result = result.replace("%b", "MMM");
28913        result = result.replace("%d", "DD");
28914        result = result.replace("%j", "DDD");
28915        result = result.replace("%H", "HH");
28916        result = result.replace("%M", "MI");
28917        result = result.replace("%S", "SS");
28918        result = result.replace("%f", "SSSSSS");
28919        result = result.replace("%A", "EEEE");
28920        result = result.replace("%a", "EEE");
28921        result
28922    }
28923
28924    /// Convert strftime format (%Y, %m, %d, etc.) to Java date format (yyyy, MM, dd, etc.)
28925    fn strftime_to_java_format(fmt: &str) -> String {
28926        let mut result = fmt.to_string();
28927        result = result.replace("%Y", "yyyy");
28928        result = result.replace("%y", "yy");
28929        result = result.replace("%m", "MM");
28930        result = result.replace("%B", "MMMM");
28931        result = result.replace("%b", "MMM");
28932        result = result.replace("%d", "dd");
28933        result = result.replace("%j", "DDD");
28934        result = result.replace("%H", "HH");
28935        result = result.replace("%M", "mm");
28936        result = result.replace("%S", "ss");
28937        result = result.replace("%f", "SSSSSS");
28938        result = result.replace("%A", "EEEE");
28939        result = result.replace("%a", "EEE");
28940        result
28941    }
28942
28943    /// Convert strftime format to PostgreSQL date format (YYYY, MM, DD, etc.)
28944    fn strftime_to_postgres_format(fmt: &str) -> String {
28945        let mut result = fmt.to_string();
28946        result = result.replace("%Y", "YYYY");
28947        result = result.replace("%y", "YY");
28948        result = result.replace("%m", "MM");
28949        result = result.replace("%B", "Month");
28950        result = result.replace("%b", "Mon");
28951        result = result.replace("%d", "DD");
28952        result = result.replace("%j", "DDD");
28953        result = result.replace("%H", "HH24");
28954        result = result.replace("%M", "MI");
28955        result = result.replace("%S", "SS");
28956        result = result.replace("%f", "US");
28957        result = result.replace("%A", "Day");
28958        result = result.replace("%a", "Dy");
28959        result
28960    }
28961
28962    fn generate_str_to_map(&mut self, e: &StrToMap) -> Result<()> {
28963        // STR_TO_MAP(this, pair_delim, key_value_delim)
28964        self.write_keyword("STR_TO_MAP");
28965        self.write("(");
28966        self.generate_expression(&e.this)?;
28967        // Spark/Hive: STR_TO_MAP needs explicit default delimiters
28968        let needs_defaults = matches!(
28969            self.config.dialect,
28970            Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)
28971        );
28972        if let Some(pair_delim) = &e.pair_delim {
28973            self.write(", ");
28974            self.generate_expression(pair_delim)?;
28975        } else if needs_defaults {
28976            self.write(", ','");
28977        }
28978        if let Some(key_value_delim) = &e.key_value_delim {
28979            self.write(", ");
28980            self.generate_expression(key_value_delim)?;
28981        } else if needs_defaults {
28982            self.write(", ':'");
28983        }
28984        self.write(")");
28985        Ok(())
28986    }
28987
28988    fn generate_str_to_time(&mut self, e: &StrToTime) -> Result<()> {
28989        match self.config.dialect {
28990            Some(DialectType::Exasol) => {
28991                self.write_keyword("TO_DATE");
28992                self.write("(");
28993                self.generate_expression(&e.this)?;
28994                self.write(", '");
28995                self.write(&Self::convert_strptime_to_exasol_format(&e.format));
28996                self.write("'");
28997                self.write(")");
28998            }
28999            Some(DialectType::BigQuery) => {
29000                // BigQuery: PARSE_TIMESTAMP(format, value) - note swapped args
29001                let fmt = Self::snowflake_format_to_strftime(&e.format);
29002                // BigQuery normalizes: %Y-%m-%d -> %F, %H:%M:%S -> %T
29003                let fmt = fmt.replace("%Y-%m-%d", "%F").replace("%H:%M:%S", "%T");
29004                self.write_keyword("PARSE_TIMESTAMP");
29005                self.write("('");
29006                self.write(&fmt);
29007                self.write("', ");
29008                self.generate_expression(&e.this)?;
29009                self.write(")");
29010            }
29011            Some(DialectType::Spark) | Some(DialectType::Databricks) => {
29012                // Spark: TO_TIMESTAMP(value, format)
29013                let fmt = Self::snowflake_format_to_spark(&e.format);
29014                self.write_keyword("TO_TIMESTAMP");
29015                self.write("(");
29016                self.generate_expression(&e.this)?;
29017                self.write(", '");
29018                self.write(&fmt);
29019                self.write("')");
29020            }
29021            Some(DialectType::Presto) | Some(DialectType::Trino) => {
29022                // Presto: DATE_PARSE(value, format)
29023                let fmt = Self::snowflake_format_to_strftime(&e.format);
29024                self.write_keyword("DATE_PARSE");
29025                self.write("(");
29026                self.generate_expression(&e.this)?;
29027                self.write(", '");
29028                self.write(&fmt);
29029                self.write("')");
29030            }
29031            Some(DialectType::DuckDB) => {
29032                // DuckDB: STRPTIME(value, format)
29033                let fmt = Self::snowflake_format_to_strftime(&e.format);
29034                self.write_keyword("STRPTIME");
29035                self.write("(");
29036                self.generate_expression(&e.this)?;
29037                self.write(", '");
29038                self.write(&fmt);
29039                self.write("')");
29040            }
29041            Some(DialectType::Snowflake) => {
29042                // Snowflake: TO_TIMESTAMP(value, format)
29043                self.write_keyword("TO_TIMESTAMP");
29044                self.write("(");
29045                self.generate_expression(&e.this)?;
29046                self.write(", '");
29047                self.write(&e.format);
29048                self.write("')");
29049            }
29050            _ => {
29051                // Default: STR_TO_TIME(this, format)
29052                self.write_keyword("STR_TO_TIME");
29053                self.write("(");
29054                self.generate_expression(&e.this)?;
29055                self.write(", '");
29056                self.write(&e.format);
29057                self.write("'");
29058                self.write(")");
29059            }
29060        }
29061        Ok(())
29062    }
29063
29064    /// Convert Snowflake normalized format to strftime-style (%Y, %m, etc.)
29065    fn snowflake_format_to_strftime(format: &str) -> String {
29066        let mut result = String::new();
29067        let chars: Vec<char> = format.chars().collect();
29068        let mut i = 0;
29069        while i < chars.len() {
29070            let remaining = &format[i..];
29071            if remaining.starts_with("yyyy") {
29072                result.push_str("%Y");
29073                i += 4;
29074            } else if remaining.starts_with("yy") {
29075                result.push_str("%y");
29076                i += 2;
29077            } else if remaining.starts_with("mmmm") {
29078                result.push_str("%B"); // full month name
29079                i += 4;
29080            } else if remaining.starts_with("mon") {
29081                result.push_str("%b"); // abbreviated month
29082                i += 3;
29083            } else if remaining.starts_with("mm") {
29084                result.push_str("%m");
29085                i += 2;
29086            } else if remaining.starts_with("DD") {
29087                result.push_str("%d");
29088                i += 2;
29089            } else if remaining.starts_with("dy") {
29090                result.push_str("%a"); // abbreviated day name
29091                i += 2;
29092            } else if remaining.starts_with("hh24") {
29093                result.push_str("%H");
29094                i += 4;
29095            } else if remaining.starts_with("hh12") {
29096                result.push_str("%I");
29097                i += 4;
29098            } else if remaining.starts_with("hh") {
29099                result.push_str("%H");
29100                i += 2;
29101            } else if remaining.starts_with("mi") {
29102                result.push_str("%M");
29103                i += 2;
29104            } else if remaining.starts_with("ss") {
29105                result.push_str("%S");
29106                i += 2;
29107            } else if remaining.starts_with("ff") {
29108                // Fractional seconds
29109                result.push_str("%f");
29110                i += 2;
29111                // Skip digits after ff (ff3, ff6, ff9)
29112                while i < chars.len() && chars[i].is_ascii_digit() {
29113                    i += 1;
29114                }
29115            } else if remaining.starts_with("am") || remaining.starts_with("pm") {
29116                result.push_str("%p");
29117                i += 2;
29118            } else if remaining.starts_with("tz") {
29119                result.push_str("%Z");
29120                i += 2;
29121            } else {
29122                result.push(chars[i]);
29123                i += 1;
29124            }
29125        }
29126        result
29127    }
29128
29129    /// Convert Snowflake normalized format to Spark format (Java-style)
29130    fn snowflake_format_to_spark(format: &str) -> String {
29131        let mut result = String::new();
29132        let chars: Vec<char> = format.chars().collect();
29133        let mut i = 0;
29134        while i < chars.len() {
29135            let remaining = &format[i..];
29136            if remaining.starts_with("yyyy") {
29137                result.push_str("yyyy");
29138                i += 4;
29139            } else if remaining.starts_with("yy") {
29140                result.push_str("yy");
29141                i += 2;
29142            } else if remaining.starts_with("mmmm") {
29143                result.push_str("MMMM"); // full month name
29144                i += 4;
29145            } else if remaining.starts_with("mon") {
29146                result.push_str("MMM"); // abbreviated month
29147                i += 3;
29148            } else if remaining.starts_with("mm") {
29149                result.push_str("MM");
29150                i += 2;
29151            } else if remaining.starts_with("DD") {
29152                result.push_str("dd");
29153                i += 2;
29154            } else if remaining.starts_with("dy") {
29155                result.push_str("EEE"); // abbreviated day name
29156                i += 2;
29157            } else if remaining.starts_with("hh24") {
29158                result.push_str("HH");
29159                i += 4;
29160            } else if remaining.starts_with("hh12") {
29161                result.push_str("hh");
29162                i += 4;
29163            } else if remaining.starts_with("hh") {
29164                result.push_str("HH");
29165                i += 2;
29166            } else if remaining.starts_with("mi") {
29167                result.push_str("mm");
29168                i += 2;
29169            } else if remaining.starts_with("ss") {
29170                result.push_str("ss");
29171                i += 2;
29172            } else if remaining.starts_with("ff") {
29173                result.push_str("SSS"); // milliseconds
29174                i += 2;
29175                // Skip digits after ff
29176                while i < chars.len() && chars[i].is_ascii_digit() {
29177                    i += 1;
29178                }
29179            } else if remaining.starts_with("am") || remaining.starts_with("pm") {
29180                result.push_str("a");
29181                i += 2;
29182            } else if remaining.starts_with("tz") {
29183                result.push_str("z");
29184                i += 2;
29185            } else {
29186                result.push(chars[i]);
29187                i += 1;
29188            }
29189        }
29190        result
29191    }
29192
29193    fn generate_str_to_unix(&mut self, e: &StrToUnix) -> Result<()> {
29194        // STR_TO_UNIX(this, format)
29195        self.write_keyword("STR_TO_UNIX");
29196        self.write("(");
29197        if let Some(this) = &e.this {
29198            self.generate_expression(this)?;
29199        }
29200        if let Some(format) = &e.format {
29201            self.write(", '");
29202            self.write(format);
29203            self.write("'");
29204        }
29205        self.write(")");
29206        Ok(())
29207    }
29208
29209    fn generate_string_to_array(&mut self, e: &StringToArray) -> Result<()> {
29210        // STRING_TO_ARRAY(this, delimiter, null_string)
29211        self.write_keyword("STRING_TO_ARRAY");
29212        self.write("(");
29213        self.generate_expression(&e.this)?;
29214        if let Some(expression) = &e.expression {
29215            self.write(", ");
29216            self.generate_expression(expression)?;
29217        }
29218        if let Some(null_val) = &e.null {
29219            self.write(", ");
29220            self.generate_expression(null_val)?;
29221        }
29222        self.write(")");
29223        Ok(())
29224    }
29225
29226    fn generate_struct(&mut self, e: &Struct) -> Result<()> {
29227        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
29228            // Snowflake: OBJECT_CONSTRUCT('key', value, 'key', value, ...)
29229            self.write_keyword("OBJECT_CONSTRUCT");
29230            self.write("(");
29231            for (i, (name, expr)) in e.fields.iter().enumerate() {
29232                if i > 0 {
29233                    self.write(", ");
29234                }
29235                if let Some(name) = name {
29236                    self.write("'");
29237                    self.write(name);
29238                    self.write("'");
29239                    self.write(", ");
29240                } else {
29241                    self.write("'_");
29242                    self.write(&i.to_string());
29243                    self.write("'");
29244                    self.write(", ");
29245                }
29246                self.generate_expression(expr)?;
29247            }
29248            self.write(")");
29249        } else if self.config.struct_curly_brace_notation {
29250            // DuckDB-style: {'key': value, ...}
29251            self.write("{");
29252            for (i, (name, expr)) in e.fields.iter().enumerate() {
29253                if i > 0 {
29254                    self.write(", ");
29255                }
29256                if let Some(name) = name {
29257                    // Quote the key as a string literal
29258                    self.write("'");
29259                    self.write(name);
29260                    self.write("'");
29261                    self.write(": ");
29262                } else {
29263                    // Unnamed field: use positional key
29264                    self.write("'_");
29265                    self.write(&i.to_string());
29266                    self.write("'");
29267                    self.write(": ");
29268                }
29269                self.generate_expression(expr)?;
29270            }
29271            self.write("}");
29272        } else {
29273            // Standard SQL struct notation
29274            // BigQuery/Spark/Databricks use: STRUCT(value AS name, ...)
29275            // Others (Presto etc.) use: STRUCT(name AS value, ...) or ROW(value, ...)
29276            let value_as_name = matches!(
29277                self.config.dialect,
29278                Some(DialectType::BigQuery)
29279                | Some(DialectType::Spark)
29280               
29281                | Some(DialectType::Databricks)
29282                | Some(DialectType::Hive)
29283            );
29284            self.write_keyword("STRUCT");
29285            self.write("(");
29286            for (i, (name, expr)) in e.fields.iter().enumerate() {
29287                if i > 0 {
29288                    self.write(", ");
29289                }
29290                if let Some(name) = name {
29291                    if value_as_name {
29292                        // STRUCT(value AS name)
29293                        self.generate_expression(expr)?;
29294                        self.write_space();
29295                        self.write_keyword("AS");
29296                        self.write_space();
29297                        // Quote name if it contains spaces or special chars
29298                        let needs_quoting = name.contains(' ') || name.contains('-');
29299                        if needs_quoting {
29300                            if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive)) {
29301                                self.write("`");
29302                                self.write(name);
29303                                self.write("`");
29304                            } else {
29305                                self.write(name);
29306                            }
29307                        } else {
29308                            self.write(name);
29309                        }
29310                    } else {
29311                        // STRUCT(name AS value)
29312                        self.write(name);
29313                        self.write_space();
29314                        self.write_keyword("AS");
29315                        self.write_space();
29316                        self.generate_expression(expr)?;
29317                    }
29318                } else {
29319                    self.generate_expression(expr)?;
29320                }
29321            }
29322            self.write(")");
29323        }
29324        Ok(())
29325    }
29326
29327    fn generate_stuff(&mut self, e: &Stuff) -> Result<()> {
29328        // STUFF(this, start, length, expression)
29329        self.write_keyword("STUFF");
29330        self.write("(");
29331        self.generate_expression(&e.this)?;
29332        if let Some(start) = &e.start {
29333            self.write(", ");
29334            self.generate_expression(start)?;
29335        }
29336        if let Some(length) = e.length {
29337            self.write(", ");
29338            self.write(&length.to_string());
29339        }
29340        self.write(", ");
29341        self.generate_expression(&e.expression)?;
29342        self.write(")");
29343        Ok(())
29344    }
29345
29346    fn generate_substring_index(&mut self, e: &SubstringIndex) -> Result<()> {
29347        // SUBSTRING_INDEX(this, delimiter, count)
29348        self.write_keyword("SUBSTRING_INDEX");
29349        self.write("(");
29350        self.generate_expression(&e.this)?;
29351        if let Some(delimiter) = &e.delimiter {
29352            self.write(", ");
29353            self.generate_expression(delimiter)?;
29354        }
29355        if let Some(count) = &e.count {
29356            self.write(", ");
29357            self.generate_expression(count)?;
29358        }
29359        self.write(")");
29360        Ok(())
29361    }
29362
29363    fn generate_summarize(&mut self, e: &Summarize) -> Result<()> {
29364        // SUMMARIZE [TABLE] this
29365        self.write_keyword("SUMMARIZE");
29366        if e.table.is_some() {
29367            self.write_space();
29368            self.write_keyword("TABLE");
29369        }
29370        self.write_space();
29371        self.generate_expression(&e.this)?;
29372        Ok(())
29373    }
29374
29375    fn generate_systimestamp(&mut self, _e: &Systimestamp) -> Result<()> {
29376        // SYSTIMESTAMP
29377        self.write_keyword("SYSTIMESTAMP");
29378        Ok(())
29379    }
29380
29381    fn generate_table_alias(&mut self, e: &TableAlias) -> Result<()> {
29382        // alias (columns...)
29383        if let Some(this) = &e.this {
29384            self.generate_expression(this)?;
29385        }
29386        if !e.columns.is_empty() {
29387            self.write("(");
29388            for (i, col) in e.columns.iter().enumerate() {
29389                if i > 0 {
29390                    self.write(", ");
29391                }
29392                self.generate_expression(col)?;
29393            }
29394            self.write(")");
29395        }
29396        Ok(())
29397    }
29398
29399    fn generate_table_from_rows(&mut self, e: &TableFromRows) -> Result<()> {
29400        // TABLE(this) [AS alias]
29401        self.write_keyword("TABLE");
29402        self.write("(");
29403        self.generate_expression(&e.this)?;
29404        self.write(")");
29405        if let Some(alias) = &e.alias {
29406            self.write_space();
29407            self.write_keyword("AS");
29408            self.write_space();
29409            self.write(alias);
29410        }
29411        Ok(())
29412    }
29413
29414    fn generate_rows_from(&mut self, e: &RowsFrom) -> Result<()> {
29415        // ROWS FROM (func1(...) AS alias1(...), func2(...) AS alias2(...)) [WITH ORDINALITY] [AS alias(...)]
29416        self.write_keyword("ROWS FROM");
29417        self.write(" (");
29418        for (i, expr) in e.expressions.iter().enumerate() {
29419            if i > 0 {
29420                self.write(", ");
29421            }
29422            // Each expression is either:
29423            // - A plain function (no alias)
29424            // - A Tuple(function, TableAlias) for: FUNC() AS alias(col type, ...)
29425            match expr {
29426                Expression::Tuple(tuple) if tuple.expressions.len() == 2 => {
29427                    // First element is the function, second is the TableAlias
29428                    self.generate_expression(&tuple.expressions[0])?;
29429                    self.write_space();
29430                    self.write_keyword("AS");
29431                    self.write_space();
29432                    self.generate_expression(&tuple.expressions[1])?;
29433                }
29434                _ => {
29435                    self.generate_expression(expr)?;
29436                }
29437            }
29438        }
29439        self.write(")");
29440        if e.ordinality {
29441            self.write_space();
29442            self.write_keyword("WITH ORDINALITY");
29443        }
29444        if let Some(alias) = &e.alias {
29445            self.write_space();
29446            self.write_keyword("AS");
29447            self.write_space();
29448            self.generate_expression(alias)?;
29449        }
29450        Ok(())
29451    }
29452
29453    fn generate_table_sample(&mut self, e: &TableSample) -> Result<()> {
29454        use crate::dialects::DialectType;
29455
29456        // New wrapper pattern: expression + Sample struct
29457        if let (Some(this), Some(sample)) = (&e.this, &e.sample) {
29458            // For alias_post_tablesample dialects (Spark, Hive, Oracle): output base expr, TABLESAMPLE, then alias
29459            if self.config.alias_post_tablesample {
29460                // Handle Subquery with alias and Alias wrapper
29461                if let Expression::Subquery(ref s) = **this {
29462                    if let Some(ref alias) = s.alias {
29463                        // Create a clone without alias for output
29464                        let mut subquery_no_alias = (**s).clone();
29465                        subquery_no_alias.alias = None;
29466                        subquery_no_alias.column_aliases = Vec::new();
29467                        self.generate_expression(&Expression::Subquery(Box::new(subquery_no_alias)))?;
29468                        self.write_space();
29469                        self.write_keyword("TABLESAMPLE");
29470                        self.generate_sample_body(sample)?;
29471                        if let Some(ref seed) = sample.seed {
29472                            self.write_space();
29473                            let use_seed = sample.use_seed_keyword
29474                                && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29475                            if use_seed { self.write_keyword("SEED"); } else { self.write_keyword("REPEATABLE"); }
29476                            self.write(" (");
29477                            self.generate_expression(seed)?;
29478                            self.write(")");
29479                        }
29480                        self.write_space();
29481                        self.write_keyword("AS");
29482                        self.write_space();
29483                        self.generate_identifier(alias)?;
29484                        return Ok(());
29485                    }
29486                } else if let Expression::Alias(ref a) = **this {
29487                    // Output the base expression without alias
29488                    self.generate_expression(&a.this)?;
29489                    self.write_space();
29490                    self.write_keyword("TABLESAMPLE");
29491                    self.generate_sample_body(sample)?;
29492                    if let Some(ref seed) = sample.seed {
29493                        self.write_space();
29494                        let use_seed = sample.use_seed_keyword
29495                            && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29496                        if use_seed { self.write_keyword("SEED"); } else { self.write_keyword("REPEATABLE"); }
29497                        self.write(" (");
29498                        self.generate_expression(seed)?;
29499                        self.write(")");
29500                    }
29501                    // Output alias after TABLESAMPLE
29502                    self.write_space();
29503                    self.write_keyword("AS");
29504                    self.write_space();
29505                    self.generate_identifier(&a.alias)?;
29506                    return Ok(());
29507                }
29508            }
29509            // Default: generate wrapped expression first, then TABLESAMPLE
29510            self.generate_expression(this)?;
29511            self.write_space();
29512            self.write_keyword("TABLESAMPLE");
29513            self.generate_sample_body(sample)?;
29514            // Seed for table-level sample
29515            if let Some(ref seed) = sample.seed {
29516                self.write_space();
29517                // Databricks uses REPEATABLE, not SEED
29518                let use_seed = sample.use_seed_keyword
29519                    && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29520                if use_seed {
29521                    self.write_keyword("SEED");
29522                } else {
29523                    self.write_keyword("REPEATABLE");
29524                }
29525                self.write(" (");
29526                self.generate_expression(seed)?;
29527                self.write(")");
29528            }
29529            return Ok(());
29530        }
29531
29532        // Legacy pattern: TABLESAMPLE [method] (expressions) or TABLESAMPLE method BUCKET numerator OUT OF denominator
29533        self.write_keyword("TABLESAMPLE");
29534        if let Some(method) = &e.method {
29535            self.write_space();
29536            self.write_keyword(method);
29537        } else if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
29538            // Snowflake defaults to BERNOULLI when no method is specified
29539            self.write_space();
29540            self.write_keyword("BERNOULLI");
29541        }
29542        if let (Some(numerator), Some(denominator)) = (&e.bucket_numerator, &e.bucket_denominator) {
29543            self.write_space();
29544            self.write_keyword("BUCKET");
29545            self.write_space();
29546            self.generate_expression(numerator)?;
29547            self.write_space();
29548            self.write_keyword("OUT OF");
29549            self.write_space();
29550            self.generate_expression(denominator)?;
29551            if let Some(field) = &e.bucket_field {
29552                self.write_space();
29553                self.write_keyword("ON");
29554                self.write_space();
29555                self.generate_expression(field)?;
29556            }
29557        } else if !e.expressions.is_empty() {
29558            self.write(" (");
29559            for (i, expr) in e.expressions.iter().enumerate() {
29560                if i > 0 {
29561                    self.write(", ");
29562                }
29563                self.generate_expression(expr)?;
29564            }
29565            self.write(")");
29566        } else if let Some(percent) = &e.percent {
29567            self.write(" (");
29568            self.generate_expression(percent)?;
29569            self.write_space();
29570            self.write_keyword("PERCENT");
29571            self.write(")");
29572        }
29573        Ok(())
29574    }
29575
29576    fn generate_tag(&mut self, e: &Tag) -> Result<()> {
29577        // [prefix]this[postfix]
29578        if let Some(prefix) = &e.prefix {
29579            self.generate_expression(prefix)?;
29580        }
29581        if let Some(this) = &e.this {
29582            self.generate_expression(this)?;
29583        }
29584        if let Some(postfix) = &e.postfix {
29585            self.generate_expression(postfix)?;
29586        }
29587        Ok(())
29588    }
29589
29590    fn generate_tags(&mut self, e: &Tags) -> Result<()> {
29591        // TAG (expressions)
29592        self.write_keyword("TAG");
29593        self.write(" (");
29594        for (i, expr) in e.expressions.iter().enumerate() {
29595            if i > 0 {
29596                self.write(", ");
29597            }
29598            self.generate_expression(expr)?;
29599        }
29600        self.write(")");
29601        Ok(())
29602    }
29603
29604    fn generate_temporary_property(&mut self, e: &TemporaryProperty) -> Result<()> {
29605        // TEMPORARY or TEMP or [this] TEMPORARY
29606        if let Some(this) = &e.this {
29607            self.generate_expression(this)?;
29608            self.write_space();
29609        }
29610        self.write_keyword("TEMPORARY");
29611        Ok(())
29612    }
29613
29614    /// Generate a Time function expression
29615    /// For most dialects: TIME('value')
29616    fn generate_time_func(&mut self, e: &UnaryFunc) -> Result<()> {
29617        // Standard: TIME(value)
29618        self.write_keyword("TIME");
29619        self.write("(");
29620        self.generate_expression(&e.this)?;
29621        self.write(")");
29622        Ok(())
29623    }
29624
29625    fn generate_time_add(&mut self, e: &TimeAdd) -> Result<()> {
29626        // TIME_ADD(this, expression, unit)
29627        self.write_keyword("TIME_ADD");
29628        self.write("(");
29629        self.generate_expression(&e.this)?;
29630        self.write(", ");
29631        self.generate_expression(&e.expression)?;
29632        if let Some(unit) = &e.unit {
29633            self.write(", ");
29634            self.write_keyword(unit);
29635        }
29636        self.write(")");
29637        Ok(())
29638    }
29639
29640    fn generate_time_diff(&mut self, e: &TimeDiff) -> Result<()> {
29641        // TIME_DIFF(this, expression, unit)
29642        self.write_keyword("TIME_DIFF");
29643        self.write("(");
29644        self.generate_expression(&e.this)?;
29645        self.write(", ");
29646        self.generate_expression(&e.expression)?;
29647        if let Some(unit) = &e.unit {
29648            self.write(", ");
29649            self.write_keyword(unit);
29650        }
29651        self.write(")");
29652        Ok(())
29653    }
29654
29655    fn generate_time_from_parts(&mut self, e: &TimeFromParts) -> Result<()> {
29656        // TIME_FROM_PARTS(hour, minute, second, nanosecond)
29657        self.write_keyword("TIME_FROM_PARTS");
29658        self.write("(");
29659        let mut first = true;
29660        if let Some(hour) = &e.hour {
29661            self.generate_expression(hour)?;
29662            first = false;
29663        }
29664        if let Some(minute) = &e.min {
29665            if !first { self.write(", "); }
29666            self.generate_expression(minute)?;
29667            first = false;
29668        }
29669        if let Some(second) = &e.sec {
29670            if !first { self.write(", "); }
29671            self.generate_expression(second)?;
29672            first = false;
29673        }
29674        if let Some(ns) = &e.nano {
29675            if !first { self.write(", "); }
29676            self.generate_expression(ns)?;
29677        }
29678        self.write(")");
29679        Ok(())
29680    }
29681
29682    fn generate_time_slice(&mut self, e: &TimeSlice) -> Result<()> {
29683        // TIME_SLICE(this, expression, unit)
29684        self.write_keyword("TIME_SLICE");
29685        self.write("(");
29686        self.generate_expression(&e.this)?;
29687        self.write(", ");
29688        self.generate_expression(&e.expression)?;
29689        self.write(", ");
29690        self.write_keyword(&e.unit);
29691        self.write(")");
29692        Ok(())
29693    }
29694
29695    fn generate_time_str_to_time(&mut self, e: &TimeStrToTime) -> Result<()> {
29696        // TIME_STR_TO_TIME(this)
29697        self.write_keyword("TIME_STR_TO_TIME");
29698        self.write("(");
29699        self.generate_expression(&e.this)?;
29700        self.write(")");
29701        Ok(())
29702    }
29703
29704    fn generate_time_sub(&mut self, e: &TimeSub) -> Result<()> {
29705        // TIME_SUB(this, expression, unit)
29706        self.write_keyword("TIME_SUB");
29707        self.write("(");
29708        self.generate_expression(&e.this)?;
29709        self.write(", ");
29710        self.generate_expression(&e.expression)?;
29711        if let Some(unit) = &e.unit {
29712            self.write(", ");
29713            self.write_keyword(unit);
29714        }
29715        self.write(")");
29716        Ok(())
29717    }
29718
29719    fn generate_time_to_str(&mut self, e: &TimeToStr) -> Result<()> {
29720        // Exasol uses TO_CHAR with Exasol-specific format
29721        if self.config.dialect == Some(DialectType::Exasol) {
29722            self.write_keyword("TO_CHAR");
29723            self.write("(");
29724            self.generate_expression(&e.this)?;
29725            self.write(", '");
29726            self.write(&Self::convert_strptime_to_exasol_format(&e.format));
29727            self.write("'");
29728            self.write(")");
29729            return Ok(());
29730        }
29731
29732        // PostgreSQL/Redshift uses TO_CHAR with PG-specific format
29733        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
29734            self.write_keyword("TO_CHAR");
29735            self.write("(");
29736            self.generate_expression(&e.this)?;
29737            self.write(", '");
29738            self.write(&Self::convert_strptime_to_postgres_format(&e.format));
29739            self.write("'");
29740            self.write(")");
29741            return Ok(());
29742        }
29743
29744        // TIME_TO_STR(this, format)
29745        self.write_keyword("TIME_TO_STR");
29746        self.write("(");
29747        self.generate_expression(&e.this)?;
29748        self.write(", '");
29749        self.write(&e.format);
29750        self.write("'");
29751        self.write(")");
29752        Ok(())
29753    }
29754
29755    fn generate_time_trunc(&mut self, e: &TimeTrunc) -> Result<()> {
29756        // TIME_TRUNC(this, unit)
29757        self.write_keyword("TIME_TRUNC");
29758        self.write("(");
29759        self.generate_expression(&e.this)?;
29760        self.write(", ");
29761        self.write_keyword(&e.unit);
29762        self.write(")");
29763        Ok(())
29764    }
29765
29766    fn generate_time_unit(&mut self, e: &TimeUnit) -> Result<()> {
29767        // Just output the unit name
29768        if let Some(unit) = &e.unit {
29769            self.write_keyword(unit);
29770        }
29771        Ok(())
29772    }
29773
29774    /// Generate a Timestamp function expression
29775    /// For Exasol: {ts'value'} -> TO_TIMESTAMP('value')
29776    /// For other dialects: TIMESTAMP('value')
29777    fn generate_timestamp_func(&mut self, e: &TimestampFunc) -> Result<()> {
29778        use crate::dialects::DialectType;
29779        use crate::expressions::Literal;
29780
29781        match self.config.dialect {
29782            // Exasol uses TO_TIMESTAMP for Timestamp expressions
29783            Some(DialectType::Exasol) => {
29784                self.write_keyword("TO_TIMESTAMP");
29785                self.write("(");
29786                // Extract the string value from the expression if it's a string literal
29787                if let Some(this) = &e.this {
29788                    match this.as_ref() {
29789                        Expression::Literal(Literal::String(s)) => {
29790                            self.write("'");
29791                            self.write(s);
29792                            self.write("'");
29793                        }
29794                        _ => {
29795                            self.generate_expression(this)?;
29796                        }
29797                    }
29798                }
29799                self.write(")");
29800            }
29801            // Standard: TIMESTAMP(value) or TIMESTAMP(value, zone)
29802            _ => {
29803                self.write_keyword("TIMESTAMP");
29804                self.write("(");
29805                if let Some(this) = &e.this {
29806                    self.generate_expression(this)?;
29807                }
29808                if let Some(zone) = &e.zone {
29809                    self.write(", ");
29810                    self.generate_expression(zone)?;
29811                }
29812                self.write(")");
29813            }
29814        }
29815        Ok(())
29816    }
29817
29818    fn generate_timestamp_add(&mut self, e: &TimestampAdd) -> Result<()> {
29819        // TIMESTAMP_ADD(this, expression, unit)
29820        self.write_keyword("TIMESTAMP_ADD");
29821        self.write("(");
29822        self.generate_expression(&e.this)?;
29823        self.write(", ");
29824        self.generate_expression(&e.expression)?;
29825        if let Some(unit) = &e.unit {
29826            self.write(", ");
29827            self.write_keyword(unit);
29828        }
29829        self.write(")");
29830        Ok(())
29831    }
29832
29833    fn generate_timestamp_diff(&mut self, e: &TimestampDiff) -> Result<()> {
29834        // TIMESTAMP_DIFF(this, expression, unit)
29835        self.write_keyword("TIMESTAMP_DIFF");
29836        self.write("(");
29837        self.generate_expression(&e.this)?;
29838        self.write(", ");
29839        self.generate_expression(&e.expression)?;
29840        if let Some(unit) = &e.unit {
29841            self.write(", ");
29842            self.write_keyword(unit);
29843        }
29844        self.write(")");
29845        Ok(())
29846    }
29847
29848    fn generate_timestamp_from_parts(&mut self, e: &TimestampFromParts) -> Result<()> {
29849        // TIMESTAMP_FROM_PARTS(this, expression)
29850        self.write_keyword("TIMESTAMP_FROM_PARTS");
29851        self.write("(");
29852        if let Some(this) = &e.this {
29853            self.generate_expression(this)?;
29854        }
29855        if let Some(expression) = &e.expression {
29856            self.write(", ");
29857            self.generate_expression(expression)?;
29858        }
29859        if let Some(zone) = &e.zone {
29860            self.write(", ");
29861            self.generate_expression(zone)?;
29862        }
29863        if let Some(milli) = &e.milli {
29864            self.write(", ");
29865            self.generate_expression(milli)?;
29866        }
29867        self.write(")");
29868        Ok(())
29869    }
29870
29871    fn generate_timestamp_sub(&mut self, e: &TimestampSub) -> Result<()> {
29872        // TIMESTAMP_SUB(this, INTERVAL expression unit)
29873        self.write_keyword("TIMESTAMP_SUB");
29874        self.write("(");
29875        self.generate_expression(&e.this)?;
29876        self.write(", ");
29877        self.write_keyword("INTERVAL");
29878        self.write_space();
29879        self.generate_expression(&e.expression)?;
29880        if let Some(unit) = &e.unit {
29881            self.write_space();
29882            self.write_keyword(unit);
29883        }
29884        self.write(")");
29885        Ok(())
29886    }
29887
29888    fn generate_timestamp_tz_from_parts(&mut self, e: &TimestampTzFromParts) -> Result<()> {
29889        // TIMESTAMP_TZ_FROM_PARTS(...)
29890        self.write_keyword("TIMESTAMP_TZ_FROM_PARTS");
29891        self.write("(");
29892        if let Some(zone) = &e.zone {
29893            self.generate_expression(zone)?;
29894        }
29895        self.write(")");
29896        Ok(())
29897    }
29898
29899    fn generate_to_binary(&mut self, e: &ToBinary) -> Result<()> {
29900        // TO_BINARY(this, [format])
29901        self.write_keyword("TO_BINARY");
29902        self.write("(");
29903        self.generate_expression(&e.this)?;
29904        if let Some(format) = &e.format {
29905            self.write(", '");
29906            self.write(format);
29907            self.write("'");
29908        }
29909        self.write(")");
29910        Ok(())
29911    }
29912
29913    fn generate_to_boolean(&mut self, e: &ToBoolean) -> Result<()> {
29914        // TO_BOOLEAN(this)
29915        self.write_keyword("TO_BOOLEAN");
29916        self.write("(");
29917        self.generate_expression(&e.this)?;
29918        self.write(")");
29919        Ok(())
29920    }
29921
29922    fn generate_to_char(&mut self, e: &ToChar) -> Result<()> {
29923        // TO_CHAR(this, [format], [nlsparam])
29924        self.write_keyword("TO_CHAR");
29925        self.write("(");
29926        self.generate_expression(&e.this)?;
29927        if let Some(format) = &e.format {
29928            self.write(", '");
29929            self.write(format);
29930            self.write("'");
29931        }
29932        if let Some(nlsparam) = &e.nlsparam {
29933            self.write(", ");
29934            self.generate_expression(nlsparam)?;
29935        }
29936        self.write(")");
29937        Ok(())
29938    }
29939
29940    fn generate_to_decfloat(&mut self, e: &ToDecfloat) -> Result<()> {
29941        // TO_DECFLOAT(this, [format])
29942        self.write_keyword("TO_DECFLOAT");
29943        self.write("(");
29944        self.generate_expression(&e.this)?;
29945        if let Some(format) = &e.format {
29946            self.write(", '");
29947            self.write(format);
29948            self.write("'");
29949        }
29950        self.write(")");
29951        Ok(())
29952    }
29953
29954    fn generate_to_double(&mut self, e: &ToDouble) -> Result<()> {
29955        // TO_DOUBLE(this, [format])
29956        self.write_keyword("TO_DOUBLE");
29957        self.write("(");
29958        self.generate_expression(&e.this)?;
29959        if let Some(format) = &e.format {
29960            self.write(", '");
29961            self.write(format);
29962            self.write("'");
29963        }
29964        self.write(")");
29965        Ok(())
29966    }
29967
29968    fn generate_to_file(&mut self, e: &ToFile) -> Result<()> {
29969        // TO_FILE(this, path)
29970        self.write_keyword("TO_FILE");
29971        self.write("(");
29972        self.generate_expression(&e.this)?;
29973        if let Some(path) = &e.path {
29974            self.write(", ");
29975            self.generate_expression(path)?;
29976        }
29977        self.write(")");
29978        Ok(())
29979    }
29980
29981    fn generate_to_number(&mut self, e: &ToNumber) -> Result<()> {
29982        // TO_NUMBER or TRY_TO_NUMBER (this, [format], [precision], [scale])
29983        // If safe flag is set, output TRY_TO_NUMBER
29984        let is_safe = e.safe.is_some();
29985        if is_safe {
29986            self.write_keyword("TRY_TO_NUMBER");
29987        } else {
29988            self.write_keyword("TO_NUMBER");
29989        }
29990        self.write("(");
29991        self.generate_expression(&e.this)?;
29992        if let Some(format) = &e.format {
29993            self.write(", ");
29994            self.generate_expression(format)?;
29995        }
29996        if let Some(nlsparam) = &e.nlsparam {
29997            self.write(", ");
29998            self.generate_expression(nlsparam)?;
29999        }
30000        if let Some(precision) = &e.precision {
30001            self.write(", ");
30002            self.generate_expression(precision)?;
30003        }
30004        if let Some(scale) = &e.scale {
30005            self.write(", ");
30006            self.generate_expression(scale)?;
30007        }
30008        self.write(")");
30009        Ok(())
30010    }
30011
30012    fn generate_to_table_property(&mut self, e: &ToTableProperty) -> Result<()> {
30013        // TO_TABLE this
30014        self.write_keyword("TO_TABLE");
30015        self.write_space();
30016        self.generate_expression(&e.this)?;
30017        Ok(())
30018    }
30019
30020    fn generate_transaction(&mut self, e: &Transaction) -> Result<()> {
30021        // Check mark to determine the format
30022        let mark_text = e.mark.as_ref().map(|m| {
30023            match m.as_ref() {
30024                Expression::Identifier(id) => id.name.clone(),
30025                Expression::Literal(Literal::String(s)) => s.clone(),
30026                _ => String::new(),
30027            }
30028        });
30029
30030        let is_start = mark_text.as_ref().map_or(false, |s| s == "START");
30031        let has_transaction_keyword = mark_text.as_ref().map_or(false, |s| s == "TRANSACTION");
30032        let has_with_mark = e.mark.as_ref().map_or(false, |m| {
30033            matches!(m.as_ref(), Expression::Literal(Literal::String(_)))
30034        });
30035
30036        if is_start {
30037            // START TRANSACTION [modes]
30038            self.write_keyword("START TRANSACTION");
30039            if let Some(modes) = &e.modes {
30040                self.write_space();
30041                self.generate_expression(modes)?;
30042            }
30043        } else {
30044            // BEGIN [DEFERRED|IMMEDIATE|EXCLUSIVE] [TRANSACTION] [transaction_name] [WITH MARK 'desc']
30045            self.write_keyword("BEGIN");
30046
30047            // Output TRANSACTION keyword if it was present
30048            if has_transaction_keyword || has_with_mark {
30049                self.write_space();
30050                self.write_keyword("TRANSACTION");
30051            }
30052
30053            // Output transaction name or kind (e.g., DEFERRED)
30054            if let Some(this) = &e.this {
30055                self.write_space();
30056                self.generate_expression(this)?;
30057            }
30058
30059            // Output WITH MARK 'description' for TSQL
30060            if has_with_mark {
30061                self.write_space();
30062                self.write_keyword("WITH MARK");
30063                if let Some(Expression::Literal(Literal::String(desc))) = e.mark.as_deref() {
30064                    if !desc.is_empty() {
30065                        self.write_space();
30066                        self.write(&format!("'{}'", desc));
30067                    }
30068                }
30069            }
30070
30071            // Output modes (isolation levels, etc.)
30072            if let Some(modes) = &e.modes {
30073                self.write_space();
30074                self.generate_expression(modes)?;
30075            }
30076        }
30077        Ok(())
30078    }
30079
30080    fn generate_transform(&mut self, e: &Transform) -> Result<()> {
30081        // TRANSFORM(this, expression)
30082        self.write_keyword("TRANSFORM");
30083        self.write("(");
30084        self.generate_expression(&e.this)?;
30085        self.write(", ");
30086        self.generate_expression(&e.expression)?;
30087        self.write(")");
30088        Ok(())
30089    }
30090
30091    fn generate_transform_model_property(&mut self, e: &TransformModelProperty) -> Result<()> {
30092        // TRANSFORM(expressions)
30093        self.write_keyword("TRANSFORM");
30094        self.write("(");
30095        if self.config.pretty && !e.expressions.is_empty() {
30096            self.indent_level += 1;
30097            for (i, expr) in e.expressions.iter().enumerate() {
30098                if i > 0 {
30099                    self.write(",");
30100                }
30101                self.write_newline();
30102                self.write_indent();
30103                self.generate_expression(expr)?;
30104            }
30105            self.indent_level -= 1;
30106            self.write_newline();
30107            self.write(")");
30108        } else {
30109            for (i, expr) in e.expressions.iter().enumerate() {
30110                if i > 0 {
30111                    self.write(", ");
30112                }
30113                self.generate_expression(expr)?;
30114            }
30115            self.write(")");
30116        }
30117        Ok(())
30118    }
30119
30120    fn generate_transient_property(&mut self, e: &TransientProperty) -> Result<()> {
30121        use crate::dialects::DialectType;
30122        // TRANSIENT is Snowflake-specific; skip for other dialects
30123        if let Some(this) = &e.this {
30124            self.generate_expression(this)?;
30125            if matches!(self.config.dialect, Some(DialectType::Snowflake) | None) {
30126                self.write_space();
30127            }
30128        }
30129        if matches!(self.config.dialect, Some(DialectType::Snowflake) | None) {
30130            self.write_keyword("TRANSIENT");
30131        }
30132        Ok(())
30133    }
30134
30135    fn generate_translate(&mut self, e: &Translate) -> Result<()> {
30136        // TRANSLATE(this, from_, to)
30137        self.write_keyword("TRANSLATE");
30138        self.write("(");
30139        self.generate_expression(&e.this)?;
30140        if let Some(from) = &e.from_ {
30141            self.write(", ");
30142            self.generate_expression(from)?;
30143        }
30144        if let Some(to) = &e.to {
30145            self.write(", ");
30146            self.generate_expression(to)?;
30147        }
30148        self.write(")");
30149        Ok(())
30150    }
30151
30152    fn generate_translate_characters(&mut self, e: &TranslateCharacters) -> Result<()> {
30153        // TRANSLATE(this USING expression)
30154        self.write_keyword("TRANSLATE");
30155        self.write("(");
30156        self.generate_expression(&e.this)?;
30157        self.write_space();
30158        self.write_keyword("USING");
30159        self.write_space();
30160        self.generate_expression(&e.expression)?;
30161        if e.with_error.is_some() {
30162            self.write_space();
30163            self.write_keyword("WITH ERROR");
30164        }
30165        self.write(")");
30166        Ok(())
30167    }
30168
30169    fn generate_truncate_table(&mut self, e: &TruncateTable) -> Result<()> {
30170        // TRUNCATE TABLE table1, table2, ...
30171        self.write_keyword("TRUNCATE TABLE");
30172        self.write_space();
30173        for (i, expr) in e.expressions.iter().enumerate() {
30174            if i > 0 {
30175                self.write(", ");
30176            }
30177            self.generate_expression(expr)?;
30178        }
30179        Ok(())
30180    }
30181
30182    fn generate_try_base64_decode_binary(&mut self, e: &TryBase64DecodeBinary) -> Result<()> {
30183        // TRY_BASE64_DECODE_BINARY(this, [alphabet])
30184        self.write_keyword("TRY_BASE64_DECODE_BINARY");
30185        self.write("(");
30186        self.generate_expression(&e.this)?;
30187        if let Some(alphabet) = &e.alphabet {
30188            self.write(", ");
30189            self.generate_expression(alphabet)?;
30190        }
30191        self.write(")");
30192        Ok(())
30193    }
30194
30195    fn generate_try_base64_decode_string(&mut self, e: &TryBase64DecodeString) -> Result<()> {
30196        // TRY_BASE64_DECODE_STRING(this, [alphabet])
30197        self.write_keyword("TRY_BASE64_DECODE_STRING");
30198        self.write("(");
30199        self.generate_expression(&e.this)?;
30200        if let Some(alphabet) = &e.alphabet {
30201            self.write(", ");
30202            self.generate_expression(alphabet)?;
30203        }
30204        self.write(")");
30205        Ok(())
30206    }
30207
30208    fn generate_try_to_decfloat(&mut self, e: &TryToDecfloat) -> Result<()> {
30209        // TRY_TO_DECFLOAT(this, [format])
30210        self.write_keyword("TRY_TO_DECFLOAT");
30211        self.write("(");
30212        self.generate_expression(&e.this)?;
30213        if let Some(format) = &e.format {
30214            self.write(", '");
30215            self.write(format);
30216            self.write("'");
30217        }
30218        self.write(")");
30219        Ok(())
30220    }
30221
30222    fn generate_ts_or_ds_add(&mut self, e: &TsOrDsAdd) -> Result<()> {
30223        // TS_OR_DS_ADD(this, expression, [unit], [return_type])
30224        self.write_keyword("TS_OR_DS_ADD");
30225        self.write("(");
30226        self.generate_expression(&e.this)?;
30227        self.write(", ");
30228        self.generate_expression(&e.expression)?;
30229        if let Some(unit) = &e.unit {
30230            self.write(", ");
30231            self.write_keyword(unit);
30232        }
30233        if let Some(return_type) = &e.return_type {
30234            self.write(", ");
30235            self.generate_expression(return_type)?;
30236        }
30237        self.write(")");
30238        Ok(())
30239    }
30240
30241    fn generate_ts_or_ds_diff(&mut self, e: &TsOrDsDiff) -> Result<()> {
30242        // TS_OR_DS_DIFF(this, expression, [unit])
30243        self.write_keyword("TS_OR_DS_DIFF");
30244        self.write("(");
30245        self.generate_expression(&e.this)?;
30246        self.write(", ");
30247        self.generate_expression(&e.expression)?;
30248        if let Some(unit) = &e.unit {
30249            self.write(", ");
30250            self.write_keyword(unit);
30251        }
30252        self.write(")");
30253        Ok(())
30254    }
30255
30256    fn generate_ts_or_ds_to_date(&mut self, e: &TsOrDsToDate) -> Result<()> {
30257        // TS_OR_DS_TO_DATE(this, [format])
30258        self.write_keyword("TS_OR_DS_TO_DATE");
30259        self.write("(");
30260        self.generate_expression(&e.this)?;
30261        if let Some(format) = &e.format {
30262            self.write(", '");
30263            self.write(format);
30264            self.write("'");
30265        }
30266        self.write(")");
30267        Ok(())
30268    }
30269
30270    fn generate_ts_or_ds_to_time(&mut self, e: &TsOrDsToTime) -> Result<()> {
30271        // TS_OR_DS_TO_TIME(this, [format])
30272        self.write_keyword("TS_OR_DS_TO_TIME");
30273        self.write("(");
30274        self.generate_expression(&e.this)?;
30275        if let Some(format) = &e.format {
30276            self.write(", '");
30277            self.write(format);
30278            self.write("'");
30279        }
30280        self.write(")");
30281        Ok(())
30282    }
30283
30284    fn generate_unhex(&mut self, e: &Unhex) -> Result<()> {
30285        // UNHEX(this, [expression])
30286        self.write_keyword("UNHEX");
30287        self.write("(");
30288        self.generate_expression(&e.this)?;
30289        if let Some(expression) = &e.expression {
30290            self.write(", ");
30291            self.generate_expression(expression)?;
30292        }
30293        self.write(")");
30294        Ok(())
30295    }
30296
30297    fn generate_unicode_string(&mut self, e: &UnicodeString) -> Result<()> {
30298        // U&this [UESCAPE escape]
30299        self.write("U&");
30300        self.generate_expression(&e.this)?;
30301        if let Some(escape) = &e.escape {
30302            self.write_space();
30303            self.write_keyword("UESCAPE");
30304            self.write_space();
30305            self.generate_expression(escape)?;
30306        }
30307        Ok(())
30308    }
30309
30310    fn generate_uniform(&mut self, e: &Uniform) -> Result<()> {
30311        // UNIFORM(this, expression, [gen], [seed])
30312        self.write_keyword("UNIFORM");
30313        self.write("(");
30314        self.generate_expression(&e.this)?;
30315        self.write(", ");
30316        self.generate_expression(&e.expression)?;
30317        if let Some(gen) = &e.gen {
30318            self.write(", ");
30319            self.generate_expression(gen)?;
30320        }
30321        if let Some(seed) = &e.seed {
30322            self.write(", ");
30323            self.generate_expression(seed)?;
30324        }
30325        self.write(")");
30326        Ok(())
30327    }
30328
30329    fn generate_unique_column_constraint(&mut self, e: &UniqueColumnConstraint) -> Result<()> {
30330        // UNIQUE [NULLS NOT DISTINCT] [this] [index_type] [on_conflict] [options]
30331        self.write_keyword("UNIQUE");
30332        // Output NULLS NOT DISTINCT if nulls is set (PostgreSQL 15+ feature)
30333        if e.nulls.is_some() {
30334            self.write(" NULLS NOT DISTINCT");
30335        }
30336        if let Some(this) = &e.this {
30337            self.write_space();
30338            self.generate_expression(this)?;
30339        }
30340        if let Some(index_type) = &e.index_type {
30341            self.write(" USING ");
30342            self.generate_expression(index_type)?;
30343        }
30344        if let Some(on_conflict) = &e.on_conflict {
30345            self.write_space();
30346            self.generate_expression(on_conflict)?;
30347        }
30348        for opt in &e.options {
30349            self.write_space();
30350            self.generate_expression(opt)?;
30351        }
30352        Ok(())
30353    }
30354
30355    fn generate_unique_key_property(&mut self, e: &UniqueKeyProperty) -> Result<()> {
30356        // UNIQUE KEY (expressions)
30357        self.write_keyword("UNIQUE KEY");
30358        self.write(" (");
30359        for (i, expr) in e.expressions.iter().enumerate() {
30360            if i > 0 {
30361                self.write(", ");
30362            }
30363            self.generate_expression(expr)?;
30364        }
30365        self.write(")");
30366        Ok(())
30367    }
30368
30369    fn generate_rollup_property(&mut self, e: &RollupProperty) -> Result<()> {
30370        // ROLLUP (r1(col1, col2), r2(col1))
30371        self.write_keyword("ROLLUP");
30372        self.write(" (");
30373        for (i, index) in e.expressions.iter().enumerate() {
30374            if i > 0 {
30375                self.write(", ");
30376            }
30377            self.generate_identifier(&index.name)?;
30378            self.write("(");
30379            for (j, col) in index.expressions.iter().enumerate() {
30380                if j > 0 {
30381                    self.write(", ");
30382                }
30383                self.generate_identifier(col)?;
30384            }
30385            self.write(")");
30386        }
30387        self.write(")");
30388        Ok(())
30389    }
30390
30391    fn generate_unix_to_str(&mut self, e: &UnixToStr) -> Result<()> {
30392        // UNIX_TO_STR(this, [format])
30393        self.write_keyword("UNIX_TO_STR");
30394        self.write("(");
30395        self.generate_expression(&e.this)?;
30396        if let Some(format) = &e.format {
30397            self.write(", '");
30398            self.write(format);
30399            self.write("'");
30400        }
30401        self.write(")");
30402        Ok(())
30403    }
30404
30405    fn generate_unix_to_time(&mut self, e: &UnixToTime) -> Result<()> {
30406        use crate::dialects::DialectType;
30407        let scale = e.scale.unwrap_or(0); // 0 = seconds
30408
30409        match self.config.dialect {
30410            Some(DialectType::Snowflake) => {
30411                // Snowflake: TO_TIMESTAMP(value[, scale]) - skip scale for seconds (0)
30412                self.write_keyword("TO_TIMESTAMP");
30413                self.write("(");
30414                self.generate_expression(&e.this)?;
30415                if let Some(s) = e.scale {
30416                    if s > 0 {
30417                        self.write(", ");
30418                        self.write(&s.to_string());
30419                    }
30420                }
30421                self.write(")");
30422            }
30423            Some(DialectType::BigQuery) => {
30424                // BigQuery: TIMESTAMP_SECONDS(value) / TIMESTAMP_MILLIS(value)
30425                // or TIMESTAMP_SECONDS(CAST(value / POWER(10, scale) AS INT64)) for other scales
30426                match scale {
30427                    0 => {
30428                        self.write_keyword("TIMESTAMP_SECONDS");
30429                        self.write("(");
30430                        self.generate_expression(&e.this)?;
30431                        self.write(")");
30432                    }
30433                    3 => {
30434                        self.write_keyword("TIMESTAMP_MILLIS");
30435                        self.write("(");
30436                        self.generate_expression(&e.this)?;
30437                        self.write(")");
30438                    }
30439                    6 => {
30440                        self.write_keyword("TIMESTAMP_MICROS");
30441                        self.write("(");
30442                        self.generate_expression(&e.this)?;
30443                        self.write(")");
30444                    }
30445                    _ => {
30446                        // TIMESTAMP_SECONDS(CAST(value / POWER(10, scale) AS INT64))
30447                        self.write_keyword("TIMESTAMP_SECONDS");
30448                        self.write("(CAST(");
30449                        self.generate_expression(&e.this)?;
30450                        self.write(&format!(" / POWER(10, {}) AS INT64))", scale));
30451                    }
30452                }
30453            }
30454            Some(DialectType::Spark) => {
30455                // Spark: CAST(FROM_UNIXTIME(value) AS TIMESTAMP) for scale=0
30456                // TIMESTAMP_MILLIS(value) for scale=3
30457                // TIMESTAMP_MICROS(value) for scale=6
30458                // TIMESTAMP_SECONDS(value / POWER(10, scale)) for other scales
30459                match scale {
30460                    0 => {
30461                        self.write_keyword("CAST");
30462                        self.write("(");
30463                        self.write_keyword("FROM_UNIXTIME");
30464                        self.write("(");
30465                        self.generate_expression(&e.this)?;
30466                        self.write(") ");
30467                        self.write_keyword("AS TIMESTAMP");
30468                        self.write(")");
30469                    }
30470                    3 => {
30471                        self.write_keyword("TIMESTAMP_MILLIS");
30472                        self.write("(");
30473                        self.generate_expression(&e.this)?;
30474                        self.write(")");
30475                    }
30476                    6 => {
30477                        self.write_keyword("TIMESTAMP_MICROS");
30478                        self.write("(");
30479                        self.generate_expression(&e.this)?;
30480                        self.write(")");
30481                    }
30482                    _ => {
30483                        self.write_keyword("TIMESTAMP_SECONDS");
30484                        self.write("(");
30485                        self.generate_expression(&e.this)?;
30486                        self.write(&format!(" / POWER(10, {}))", scale));
30487                    }
30488                }
30489            }
30490            Some(DialectType::Databricks) => {
30491                // Databricks: CAST(FROM_UNIXTIME(value) AS TIMESTAMP) for scale=0
30492                // TIMESTAMP_MILLIS(value) for scale=3
30493                // TIMESTAMP_MICROS(value) for scale=6
30494                match scale {
30495                    0 => {
30496                        self.write_keyword("CAST");
30497                        self.write("(");
30498                        self.write_keyword("FROM_UNIXTIME");
30499                        self.write("(");
30500                        self.generate_expression(&e.this)?;
30501                        self.write(") ");
30502                        self.write_keyword("AS TIMESTAMP");
30503                        self.write(")");
30504                    }
30505                    3 => {
30506                        self.write_keyword("TIMESTAMP_MILLIS");
30507                        self.write("(");
30508                        self.generate_expression(&e.this)?;
30509                        self.write(")");
30510                    }
30511                    6 => {
30512                        self.write_keyword("TIMESTAMP_MICROS");
30513                        self.write("(");
30514                        self.generate_expression(&e.this)?;
30515                        self.write(")");
30516                    }
30517                    _ => {
30518                        self.write_keyword("TIMESTAMP_SECONDS");
30519                        self.write("(");
30520                        self.generate_expression(&e.this)?;
30521                        self.write(&format!(" / POWER(10, {}))", scale));
30522                    }
30523                }
30524            }
30525            Some(DialectType::Presto) | Some(DialectType::Trino) => {
30526                // Presto: FROM_UNIXTIME(CAST(value AS DOUBLE) / POW(10, scale)) for scale > 0
30527                // FROM_UNIXTIME(value) for scale=0
30528                if scale == 0 {
30529                    self.write_keyword("FROM_UNIXTIME");
30530                    self.write("(");
30531                    self.generate_expression(&e.this)?;
30532                    self.write(")");
30533                } else {
30534                    self.write_keyword("FROM_UNIXTIME");
30535                    self.write("(CAST(");
30536                    self.generate_expression(&e.this)?;
30537                    self.write(&format!(" AS DOUBLE) / POW(10, {}))", scale));
30538                }
30539            }
30540            Some(DialectType::DuckDB) => {
30541                // DuckDB: TO_TIMESTAMP(value) for scale=0
30542                // EPOCH_MS(value) for scale=3
30543                // MAKE_TIMESTAMP(value) for scale=6
30544                match scale {
30545                    0 => {
30546                        self.write_keyword("TO_TIMESTAMP");
30547                        self.write("(");
30548                        self.generate_expression(&e.this)?;
30549                        self.write(")");
30550                    }
30551                    3 => {
30552                        self.write_keyword("EPOCH_MS");
30553                        self.write("(");
30554                        self.generate_expression(&e.this)?;
30555                        self.write(")");
30556                    }
30557                    6 => {
30558                        self.write_keyword("MAKE_TIMESTAMP");
30559                        self.write("(");
30560                        self.generate_expression(&e.this)?;
30561                        self.write(")");
30562                    }
30563                    _ => {
30564                        self.write_keyword("TO_TIMESTAMP");
30565                        self.write("(");
30566                        self.generate_expression(&e.this)?;
30567                        self.write(&format!(" / POWER(10, {}))", scale));
30568                        self.write_keyword(" AT TIME ZONE");
30569                        self.write(" 'UTC'");
30570                    }
30571                }
30572            }
30573            Some(DialectType::Redshift) => {
30574                // Redshift: (TIMESTAMP 'epoch' + value * INTERVAL '1 SECOND') for scale=0
30575                // (TIMESTAMP 'epoch' + (value / POWER(10, scale)) * INTERVAL '1 SECOND') for scale > 0
30576                self.write("(TIMESTAMP 'epoch' + ");
30577                if scale == 0 {
30578                    self.generate_expression(&e.this)?;
30579                } else {
30580                    self.write("(");
30581                    self.generate_expression(&e.this)?;
30582                    self.write(&format!(" / POWER(10, {}))", scale));
30583                }
30584                self.write(" * INTERVAL '1 SECOND')");
30585            }
30586            _ => {
30587                // Default: TO_TIMESTAMP(value[, scale])
30588                self.write_keyword("TO_TIMESTAMP");
30589                self.write("(");
30590                self.generate_expression(&e.this)?;
30591                if let Some(s) = e.scale {
30592                    self.write(", ");
30593                    self.write(&s.to_string());
30594                }
30595                self.write(")");
30596            }
30597        }
30598        Ok(())
30599    }
30600
30601    fn generate_unpivot_columns(&mut self, e: &UnpivotColumns) -> Result<()> {
30602        // NAME col VALUE col1, col2, ...
30603        if !matches!(&*e.this, Expression::Null(_)) {
30604            self.write_keyword("NAME");
30605            self.write_space();
30606            self.generate_expression(&e.this)?;
30607        }
30608        if !e.expressions.is_empty() {
30609            self.write_space();
30610            self.write_keyword("VALUE");
30611            self.write_space();
30612            for (i, expr) in e.expressions.iter().enumerate() {
30613                if i > 0 {
30614                    self.write(", ");
30615                }
30616                self.generate_expression(expr)?;
30617            }
30618        }
30619        Ok(())
30620    }
30621
30622    fn generate_user_defined_function(&mut self, e: &UserDefinedFunction) -> Result<()> {
30623        // this(expressions) or (this)(expressions)
30624        if e.wrapped.is_some() {
30625            self.write("(");
30626        }
30627        self.generate_expression(&e.this)?;
30628        if e.wrapped.is_some() {
30629            self.write(")");
30630        }
30631        self.write("(");
30632        for (i, expr) in e.expressions.iter().enumerate() {
30633            if i > 0 {
30634                self.write(", ");
30635            }
30636            self.generate_expression(expr)?;
30637        }
30638        self.write(")");
30639        Ok(())
30640    }
30641
30642    fn generate_using_template_property(&mut self, e: &UsingTemplateProperty) -> Result<()> {
30643        // USING TEMPLATE this
30644        self.write_keyword("USING TEMPLATE");
30645        self.write_space();
30646        self.generate_expression(&e.this)?;
30647        Ok(())
30648    }
30649
30650    fn generate_utc_time(&mut self, _e: &UtcTime) -> Result<()> {
30651        // UTC_TIME
30652        self.write_keyword("UTC_TIME");
30653        Ok(())
30654    }
30655
30656    fn generate_utc_timestamp(&mut self, _e: &UtcTimestamp) -> Result<()> {
30657        // UTC_TIMESTAMP
30658        self.write_keyword("UTC_TIMESTAMP");
30659        Ok(())
30660    }
30661
30662    fn generate_uuid(&mut self, e: &Uuid) -> Result<()> {
30663        use crate::dialects::DialectType;
30664        // Choose UUID function name based on target dialect
30665        let func_name = match self.config.dialect {
30666            Some(DialectType::Snowflake) => "UUID_STRING",
30667            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => "GEN_RANDOM_UUID",
30668            Some(DialectType::BigQuery) => "GENERATE_UUID",
30669            _ => {
30670                if let Some(name) = &e.name {
30671                    name.as_str()
30672                } else {
30673                    "UUID"
30674                }
30675            }
30676        };
30677        self.write_keyword(func_name);
30678        self.write("(");
30679        if let Some(this) = &e.this {
30680            self.generate_expression(this)?;
30681        }
30682        self.write(")");
30683        Ok(())
30684    }
30685
30686    fn generate_var_map(&mut self, e: &VarMap) -> Result<()> {
30687        // MAP(key1, value1, key2, value2, ...)
30688        self.write_keyword("MAP");
30689        self.write("(");
30690        let mut first = true;
30691        for (k, v) in e.keys.iter().zip(e.values.iter()) {
30692            if !first {
30693                self.write(", ");
30694            }
30695            self.generate_expression(k)?;
30696            self.write(", ");
30697            self.generate_expression(v)?;
30698            first = false;
30699        }
30700        self.write(")");
30701        Ok(())
30702    }
30703
30704    fn generate_vector_search(&mut self, e: &VectorSearch) -> Result<()> {
30705        // VECTOR_SEARCH(this, column_to_search, query_table, query_column_to_search, top_k, distance_type, ...)
30706        self.write_keyword("VECTOR_SEARCH");
30707        self.write("(");
30708        self.generate_expression(&e.this)?;
30709        if let Some(col) = &e.column_to_search {
30710            self.write(", ");
30711            self.generate_expression(col)?;
30712        }
30713        if let Some(query_table) = &e.query_table {
30714            self.write(", ");
30715            self.generate_expression(query_table)?;
30716        }
30717        if let Some(query_col) = &e.query_column_to_search {
30718            self.write(", ");
30719            self.generate_expression(query_col)?;
30720        }
30721        if let Some(top_k) = &e.top_k {
30722            self.write(", ");
30723            self.generate_expression(top_k)?;
30724        }
30725        if let Some(dist_type) = &e.distance_type {
30726            self.write(", ");
30727            self.generate_expression(dist_type)?;
30728        }
30729        self.write(")");
30730        Ok(())
30731    }
30732
30733    fn generate_version(&mut self, e: &Version) -> Result<()> {
30734        // Python: f"FOR {expression.name} {kind} {expr}"
30735        // e.this = Identifier("TIMESTAMP" or "VERSION")
30736        // e.kind = "AS OF" (or "BETWEEN", etc.)
30737        // e.expression = the value expression
30738        // Hive does NOT use the FOR prefix for time travel
30739        use crate::dialects::DialectType;
30740        let skip_for = matches!(self.config.dialect, Some(DialectType::Hive) | Some(DialectType::Spark));
30741        if !skip_for {
30742            self.write_keyword("FOR");
30743            self.write_space();
30744        }
30745        // Extract the name from this (which is an Identifier expression)
30746        match e.this.as_ref() {
30747            Expression::Identifier(ident) => {
30748                self.write_keyword(&ident.name);
30749            }
30750            _ => {
30751                self.generate_expression(&e.this)?;
30752            }
30753        }
30754        self.write_space();
30755        self.write_keyword(&e.kind);
30756        if let Some(expression) = &e.expression {
30757            self.write_space();
30758            self.generate_expression(expression)?;
30759        }
30760        Ok(())
30761    }
30762
30763    fn generate_view_attribute_property(&mut self, e: &ViewAttributeProperty) -> Result<()> {
30764        // Python: return self.sql(expression, "this")
30765        self.generate_expression(&e.this)?;
30766        Ok(())
30767    }
30768
30769    fn generate_volatile_property(&mut self, e: &VolatileProperty) -> Result<()> {
30770        // Python: return "VOLATILE" if expression.args.get("this") is None else "NOT VOLATILE"
30771        if e.this.is_some() {
30772            self.write_keyword("NOT VOLATILE");
30773        } else {
30774            self.write_keyword("VOLATILE");
30775        }
30776        Ok(())
30777    }
30778
30779    fn generate_watermark_column_constraint(&mut self, e: &WatermarkColumnConstraint) -> Result<()> {
30780        // Python: f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
30781        self.write_keyword("WATERMARK FOR");
30782        self.write_space();
30783        self.generate_expression(&e.this)?;
30784        self.write_space();
30785        self.write_keyword("AS");
30786        self.write_space();
30787        self.generate_expression(&e.expression)?;
30788        Ok(())
30789    }
30790
30791    fn generate_week(&mut self, e: &Week) -> Result<()> {
30792        // Python: return self.func("WEEK", expression.this, expression.args.get("mode"))
30793        self.write_keyword("WEEK");
30794        self.write("(");
30795        self.generate_expression(&e.this)?;
30796        if let Some(mode) = &e.mode {
30797            self.write(", ");
30798            self.generate_expression(mode)?;
30799        }
30800        self.write(")");
30801        Ok(())
30802    }
30803
30804    fn generate_when(&mut self, e: &When) -> Result<()> {
30805        // Python: WHEN {matched}{source}{condition} THEN {then}
30806        // matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
30807        // source = " BY SOURCE" if MATCHED_BY_SOURCE and expression.args.get("source") else ""
30808        self.write_keyword("WHEN");
30809        self.write_space();
30810
30811        // Check if matched
30812        if let Some(matched) = &e.matched {
30813            // Check the expression - if it's a boolean true, use MATCHED, otherwise NOT MATCHED
30814            match matched.as_ref() {
30815                Expression::Boolean(b) if b.value => {
30816                    self.write_keyword("MATCHED");
30817                }
30818                _ => {
30819                    self.write_keyword("NOT MATCHED");
30820                }
30821            }
30822        } else {
30823            self.write_keyword("NOT MATCHED");
30824        }
30825
30826        // BY SOURCE / BY TARGET
30827        // source = Boolean(true) means BY SOURCE, Boolean(false) means BY TARGET
30828        // BY TARGET is the default and typically omitted in output
30829        // Only emit if the dialect supports BY SOURCE syntax
30830        if self.config.matched_by_source {
30831            if let Some(source) = &e.source {
30832                if let Expression::Boolean(b) = source.as_ref() {
30833                    if b.value {
30834                        // BY SOURCE
30835                        self.write_space();
30836                        self.write_keyword("BY SOURCE");
30837                    }
30838                    // BY TARGET (b.value == false) is omitted as it's the default
30839                } else {
30840                    // For non-boolean source, output as BY SOURCE (legacy behavior)
30841                    self.write_space();
30842                    self.write_keyword("BY SOURCE");
30843                }
30844            }
30845        }
30846
30847        // Condition
30848        if let Some(condition) = &e.condition {
30849            self.write_space();
30850            self.write_keyword("AND");
30851            self.write_space();
30852            self.generate_expression(condition)?;
30853        }
30854
30855        self.write_space();
30856        self.write_keyword("THEN");
30857        self.write_space();
30858
30859        // Generate the then expression (could be INSERT, UPDATE, DELETE)
30860        // MERGE actions are stored as Tuples with the action keyword as first element
30861        self.generate_merge_action(&e.then)?;
30862
30863        Ok(())
30864    }
30865
30866    fn generate_merge_action(&mut self, action: &Expression) -> Result<()> {
30867        match action {
30868            Expression::Tuple(tuple) => {
30869                let elements = &tuple.expressions;
30870                if elements.is_empty() {
30871                    return self.generate_expression(action);
30872                }
30873                // Check if first element is a Var (INSERT, UPDATE, DELETE, etc.)
30874                match &elements[0] {
30875                    Expression::Var(v) if v.this == "INSERT" => {
30876                        self.write_keyword("INSERT");
30877                        let mut values_idx = 1;
30878                        // Check if second element is column list (Tuple)
30879                        if elements.len() > 1 {
30880                            if let Expression::Tuple(cols) = &elements[1] {
30881                                // Could be columns or values - if there's a third element, second is columns
30882                                if elements.len() > 2 {
30883                                    // Second is columns, third is values
30884                                    self.write(" (");
30885                                    for (i, col) in cols.expressions.iter().enumerate() {
30886                                        if i > 0 { self.write(", "); }
30887                                        self.generate_expression(col)?;
30888                                    }
30889                                    self.write(")");
30890                                    values_idx = 2;
30891                                } else {
30892                                    // Only two elements: INSERT + values (no explicit columns)
30893                                    values_idx = 1;
30894                                }
30895                            }
30896                        }
30897                        // Generate VALUES clause
30898                        if values_idx < elements.len() {
30899                            // Check if it's INSERT ROW (BigQuery) — no VALUES keyword needed
30900                            let is_row = matches!(&elements[values_idx], Expression::Var(v) if v.this == "ROW");
30901                            if !is_row {
30902                                self.write_space();
30903                                self.write_keyword("VALUES");
30904                            }
30905                            self.write(" ");
30906                            if let Expression::Tuple(vals) = &elements[values_idx] {
30907                                self.write("(");
30908                                for (i, val) in vals.expressions.iter().enumerate() {
30909                                    if i > 0 { self.write(", "); }
30910                                    self.generate_expression(val)?;
30911                                }
30912                                self.write(")");
30913                            } else {
30914                                self.generate_expression(&elements[values_idx])?;
30915                            }
30916                        }
30917                    }
30918                    Expression::Var(v) if v.this == "UPDATE" => {
30919                        self.write_keyword("UPDATE");
30920                        if elements.len() > 1 {
30921                            self.write_space();
30922                            self.write_keyword("SET");
30923                            // In pretty mode, put assignments on next line with extra indent
30924                            if self.config.pretty {
30925                                self.write_newline();
30926                                self.indent_level += 1;
30927                                self.write_indent();
30928                            } else {
30929                                self.write_space();
30930                            }
30931                            if let Expression::Tuple(assignments) = &elements[1] {
30932                                for (i, assignment) in assignments.expressions.iter().enumerate() {
30933                                    if i > 0 { self.write(", "); }
30934                                    // Strip MERGE target qualifiers from left side of UPDATE SET
30935                                    if !self.merge_strip_qualifiers.is_empty() {
30936                                        self.generate_merge_set_assignment(assignment)?;
30937                                    } else {
30938                                        self.generate_expression(assignment)?;
30939                                    }
30940                                }
30941                            } else {
30942                                self.generate_expression(&elements[1])?;
30943                            }
30944                            if self.config.pretty {
30945                                self.indent_level -= 1;
30946                            }
30947                        }
30948                    }
30949                    _ => {
30950                        // Fallback: generic tuple generation
30951                        self.generate_expression(action)?;
30952                    }
30953                }
30954            }
30955            Expression::Var(v) if v.this == "INSERT" || v.this == "UPDATE" || v.this == "DELETE" || v.this == "DO NOTHING" => {
30956                self.write_keyword(&v.this);
30957            }
30958            _ => {
30959                self.generate_expression(action)?;
30960            }
30961        }
30962        Ok(())
30963    }
30964
30965    /// Generate a MERGE UPDATE SET assignment, stripping target table qualifier from left side
30966    fn generate_merge_set_assignment(&mut self, assignment: &Expression) -> Result<()> {
30967        match assignment {
30968            Expression::Eq(eq) => {
30969                // Strip qualifier from the left side if it matches a MERGE target name
30970                let stripped_left = self.strip_merge_qualifier(&eq.left);
30971                self.generate_expression(&stripped_left)?;
30972                self.write(" = ");
30973                self.generate_expression(&eq.right)?;
30974                Ok(())
30975            }
30976            other => self.generate_expression(other),
30977        }
30978    }
30979
30980    /// Strip table qualifier from a column reference if it matches a MERGE target name
30981    fn strip_merge_qualifier(&self, expr: &Expression) -> Expression {
30982        match expr {
30983            Expression::Column(col) => {
30984                if let Some(ref table_ident) = col.table {
30985                    if self.merge_strip_qualifiers.iter().any(|n| n.eq_ignore_ascii_case(&table_ident.name)) {
30986                        // Strip the table qualifier
30987                        let mut col = col.clone();
30988                        col.table = None;
30989                        return Expression::Column(col);
30990                    }
30991                }
30992                expr.clone()
30993            }
30994            Expression::Dot(dot) => {
30995                // table.column -> column (strip qualifier)
30996                if let Expression::Identifier(id) = &dot.this {
30997                    if self.merge_strip_qualifiers.iter().any(|n| n.eq_ignore_ascii_case(&id.name)) {
30998                        return Expression::Identifier(dot.field.clone());
30999                    }
31000                }
31001                expr.clone()
31002            }
31003            _ => expr.clone(),
31004        }
31005    }
31006
31007    fn generate_whens(&mut self, e: &Whens) -> Result<()> {
31008        // Python: return self.expressions(expression, sep=" ", indent=False)
31009        for (i, expr) in e.expressions.iter().enumerate() {
31010            if i > 0 {
31011                // In pretty mode, each WHEN clause on its own line
31012                if self.config.pretty {
31013                    self.write_newline();
31014                    self.write_indent();
31015                } else {
31016                    self.write_space();
31017                }
31018            }
31019            self.generate_expression(expr)?;
31020        }
31021        Ok(())
31022    }
31023
31024    fn generate_where(&mut self, e: &Where) -> Result<()> {
31025        // Python: return f"{self.seg('WHERE')}{self.sep()}{this}"
31026        self.write_keyword("WHERE");
31027        self.write_space();
31028        self.generate_expression(&e.this)?;
31029        Ok(())
31030    }
31031
31032    fn generate_width_bucket(&mut self, e: &WidthBucket) -> Result<()> {
31033        // Python: return self.func("WIDTH_BUCKET", expression.this, ...)
31034        self.write_keyword("WIDTH_BUCKET");
31035        self.write("(");
31036        self.generate_expression(&e.this)?;
31037        if let Some(min_value) = &e.min_value {
31038            self.write(", ");
31039            self.generate_expression(min_value)?;
31040        }
31041        if let Some(max_value) = &e.max_value {
31042            self.write(", ");
31043            self.generate_expression(max_value)?;
31044        }
31045        if let Some(num_buckets) = &e.num_buckets {
31046            self.write(", ");
31047            self.generate_expression(num_buckets)?;
31048        }
31049        self.write(")");
31050        Ok(())
31051    }
31052
31053    fn generate_window(&mut self, e: &WindowSpec) -> Result<()> {
31054        // Window specification: PARTITION BY ... ORDER BY ... frame
31055        self.generate_window_spec(e)
31056    }
31057
31058    fn generate_window_spec(&mut self, e: &WindowSpec) -> Result<()> {
31059        // Window specification: PARTITION BY ... ORDER BY ... frame
31060        let mut has_content = false;
31061
31062        // PARTITION BY
31063        if !e.partition_by.is_empty() {
31064            self.write_keyword("PARTITION BY");
31065            self.write_space();
31066            for (i, expr) in e.partition_by.iter().enumerate() {
31067                if i > 0 {
31068                    self.write(", ");
31069                }
31070                self.generate_expression(expr)?;
31071            }
31072            has_content = true;
31073        }
31074
31075        // ORDER BY
31076        if !e.order_by.is_empty() {
31077            if has_content {
31078                self.write_space();
31079            }
31080            self.write_keyword("ORDER BY");
31081            self.write_space();
31082            for (i, ordered) in e.order_by.iter().enumerate() {
31083                if i > 0 {
31084                    self.write(", ");
31085                }
31086                self.generate_expression(&ordered.this)?;
31087                if ordered.desc {
31088                    self.write_space();
31089                    self.write_keyword("DESC");
31090                } else if ordered.explicit_asc {
31091                    self.write_space();
31092                    self.write_keyword("ASC");
31093                }
31094                if let Some(nulls_first) = ordered.nulls_first {
31095                    self.write_space();
31096                    self.write_keyword("NULLS");
31097                    self.write_space();
31098                    if nulls_first {
31099                        self.write_keyword("FIRST");
31100                    } else {
31101                        self.write_keyword("LAST");
31102                    }
31103                }
31104            }
31105            has_content = true;
31106        }
31107
31108        // Frame specification
31109        if let Some(frame) = &e.frame {
31110            if has_content {
31111                self.write_space();
31112            }
31113            self.generate_window_frame(frame)?;
31114        }
31115
31116        Ok(())
31117    }
31118
31119    fn generate_with_data_property(&mut self, e: &WithDataProperty) -> Result<()> {
31120        // Python: f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
31121        self.write_keyword("WITH");
31122        self.write_space();
31123        if e.no.is_some() {
31124            self.write_keyword("NO");
31125            self.write_space();
31126        }
31127        self.write_keyword("DATA");
31128
31129        // statistics
31130        if let Some(statistics) = &e.statistics {
31131            self.write_space();
31132            self.write_keyword("AND");
31133            self.write_space();
31134            // Check if statistics is true or false
31135            match statistics.as_ref() {
31136                Expression::Boolean(b) if !b.value => {
31137                    self.write_keyword("NO");
31138                    self.write_space();
31139                }
31140                _ => {}
31141            }
31142            self.write_keyword("STATISTICS");
31143        }
31144        Ok(())
31145    }
31146
31147    fn generate_with_fill(&mut self, e: &WithFill) -> Result<()> {
31148        // Python: f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
31149        self.write_keyword("WITH FILL");
31150
31151        if let Some(from_) = &e.from_ {
31152            self.write_space();
31153            self.write_keyword("FROM");
31154            self.write_space();
31155            self.generate_expression(from_)?;
31156        }
31157
31158        if let Some(to) = &e.to {
31159            self.write_space();
31160            self.write_keyword("TO");
31161            self.write_space();
31162            self.generate_expression(to)?;
31163        }
31164
31165        if let Some(step) = &e.step {
31166            self.write_space();
31167            self.write_keyword("STEP");
31168            self.write_space();
31169            self.generate_expression(step)?;
31170        }
31171
31172        if let Some(interpolate) = &e.interpolate {
31173            self.write_space();
31174            self.write_keyword("INTERPOLATE");
31175            self.write(" (");
31176            // INTERPOLATE items use reversed alias format: name AS expression
31177            self.generate_interpolate_item(interpolate)?;
31178            self.write(")");
31179        }
31180
31181        Ok(())
31182    }
31183
31184    /// Generate INTERPOLATE items with reversed alias format (name AS expression)
31185    fn generate_interpolate_item(&mut self, expr: &Expression) -> Result<()> {
31186        match expr {
31187            Expression::Alias(alias) => {
31188                // Output as: alias_name AS expression
31189                self.generate_identifier(&alias.alias)?;
31190                self.write_space();
31191                self.write_keyword("AS");
31192                self.write_space();
31193                self.generate_expression(&alias.this)?;
31194            }
31195            Expression::Tuple(tuple) => {
31196                for (i, item) in tuple.expressions.iter().enumerate() {
31197                    if i > 0 { self.write(", "); }
31198                    self.generate_interpolate_item(item)?;
31199                }
31200            }
31201            other => {
31202                self.generate_expression(other)?;
31203            }
31204        }
31205        Ok(())
31206    }
31207
31208    fn generate_with_journal_table_property(&mut self, e: &WithJournalTableProperty) -> Result<()> {
31209        // Python: return f"WITH JOURNAL TABLE={self.sql(expression, 'this')}"
31210        self.write_keyword("WITH JOURNAL TABLE");
31211        self.write("=");
31212        self.generate_expression(&e.this)?;
31213        Ok(())
31214    }
31215
31216    fn generate_with_operator(&mut self, e: &WithOperator) -> Result<()> {
31217        // Python: return f"{self.sql(expression, 'this')} WITH {self.sql(expression, 'op')}"
31218        self.generate_expression(&e.this)?;
31219        self.write_space();
31220        self.write_keyword("WITH");
31221        self.write_space();
31222        self.write_keyword(&e.op);
31223        Ok(())
31224    }
31225
31226    fn generate_with_procedure_options(&mut self, e: &WithProcedureOptions) -> Result<()> {
31227        // Python: return f"WITH {self.expressions(expression, flat=True)}"
31228        self.write_keyword("WITH");
31229        self.write_space();
31230        for (i, expr) in e.expressions.iter().enumerate() {
31231            if i > 0 {
31232                self.write(", ");
31233            }
31234            self.generate_expression(expr)?;
31235        }
31236        Ok(())
31237    }
31238
31239    fn generate_with_schema_binding_property(&mut self, e: &WithSchemaBindingProperty) -> Result<()> {
31240        // Python: return f"WITH {self.sql(expression, 'this')}"
31241        self.write_keyword("WITH");
31242        self.write_space();
31243        self.generate_expression(&e.this)?;
31244        Ok(())
31245    }
31246
31247    fn generate_with_system_versioning_property(&mut self, e: &WithSystemVersioningProperty) -> Result<()> {
31248        // Python: complex logic for SYSTEM_VERSIONING with options
31249        // SYSTEM_VERSIONING=ON(HISTORY_TABLE=..., DATA_CONSISTENCY_CHECK=..., HISTORY_RETENTION_PERIOD=...)
31250        // or SYSTEM_VERSIONING=ON/OFF
31251        // with WITH(...) wrapper if with_ is set
31252
31253        let mut parts = Vec::new();
31254
31255        if let Some(this) = &e.this {
31256            // HISTORY_TABLE=...
31257            let mut s = String::from("HISTORY_TABLE=");
31258            let mut gen = Generator::new();
31259            gen.generate_expression(this)?;
31260            s.push_str(&gen.output);
31261            parts.push(s);
31262        }
31263
31264        if let Some(data_consistency) = &e.data_consistency {
31265            let mut s = String::from("DATA_CONSISTENCY_CHECK=");
31266            let mut gen = Generator::new();
31267            gen.generate_expression(data_consistency)?;
31268            s.push_str(&gen.output);
31269            parts.push(s);
31270        }
31271
31272        if let Some(retention_period) = &e.retention_period {
31273            let mut s = String::from("HISTORY_RETENTION_PERIOD=");
31274            let mut gen = Generator::new();
31275            gen.generate_expression(retention_period)?;
31276            s.push_str(&gen.output);
31277            parts.push(s);
31278        }
31279
31280        self.write_keyword("SYSTEM_VERSIONING");
31281        self.write("=");
31282
31283        if !parts.is_empty() {
31284            self.write_keyword("ON");
31285            self.write("(");
31286            self.write(&parts.join(", "));
31287            self.write(")");
31288        } else if e.on.is_some() {
31289            self.write_keyword("ON");
31290        } else {
31291            self.write_keyword("OFF");
31292        }
31293
31294        // Wrap in WITH(...) if with_ is set
31295        if e.with_.is_some() {
31296            let inner = self.output.clone();
31297            self.output.clear();
31298            self.write("WITH(");
31299            self.write(&inner);
31300            self.write(")");
31301        }
31302
31303        Ok(())
31304    }
31305
31306    fn generate_with_table_hint(&mut self, e: &WithTableHint) -> Result<()> {
31307        // Python: f"WITH ({self.expressions(expression, flat=True)})"
31308        self.write_keyword("WITH");
31309        self.write(" (");
31310        for (i, expr) in e.expressions.iter().enumerate() {
31311            if i > 0 {
31312                self.write(", ");
31313            }
31314            self.generate_expression(expr)?;
31315        }
31316        self.write(")");
31317        Ok(())
31318    }
31319
31320    fn generate_xml_element(&mut self, e: &XMLElement) -> Result<()> {
31321        // Python: prefix = "EVALNAME" if expression.args.get("evalname") else "NAME"
31322        // return self.func("XMLELEMENT", name, *expression.expressions)
31323        self.write_keyword("XMLELEMENT");
31324        self.write("(");
31325
31326        if e.evalname.is_some() {
31327            self.write_keyword("EVALNAME");
31328        } else {
31329            self.write_keyword("NAME");
31330        }
31331        self.write_space();
31332        self.generate_expression(&e.this)?;
31333
31334        for expr in &e.expressions {
31335            self.write(", ");
31336            self.generate_expression(expr)?;
31337        }
31338        self.write(")");
31339        Ok(())
31340    }
31341
31342    fn generate_xml_get(&mut self, e: &XMLGet) -> Result<()> {
31343        // XMLGET(this, expression [, instance])
31344        self.write_keyword("XMLGET");
31345        self.write("(");
31346        self.generate_expression(&e.this)?;
31347        self.write(", ");
31348        self.generate_expression(&e.expression)?;
31349        if let Some(instance) = &e.instance {
31350            self.write(", ");
31351            self.generate_expression(instance)?;
31352        }
31353        self.write(")");
31354        Ok(())
31355    }
31356
31357    fn generate_xml_key_value_option(&mut self, e: &XMLKeyValueOption) -> Result<()> {
31358        // Python: this + optional (expr)
31359        self.generate_expression(&e.this)?;
31360        if let Some(expression) = &e.expression {
31361            self.write("(");
31362            self.generate_expression(expression)?;
31363            self.write(")");
31364        }
31365        Ok(())
31366    }
31367
31368    fn generate_xml_table(&mut self, e: &XMLTable) -> Result<()> {
31369        // Python: XMLTABLE(namespaces + this + passing + by_ref + columns)
31370        self.write_keyword("XMLTABLE");
31371        self.write("(");
31372
31373        if self.config.pretty {
31374            self.indent_level += 1;
31375            self.write_newline();
31376            self.write_indent();
31377            self.generate_expression(&e.this)?;
31378
31379            if let Some(passing) = &e.passing {
31380                self.write_newline();
31381                self.write_indent();
31382                self.write_keyword("PASSING");
31383                if let Expression::Tuple(tuple) = passing.as_ref() {
31384                    for expr in &tuple.expressions {
31385                        self.write_newline();
31386                        self.indent_level += 1;
31387                        self.write_indent();
31388                        self.generate_expression(expr)?;
31389                        self.indent_level -= 1;
31390                    }
31391                } else {
31392                    self.write_newline();
31393                    self.indent_level += 1;
31394                    self.write_indent();
31395                    self.generate_expression(passing)?;
31396                    self.indent_level -= 1;
31397                }
31398            }
31399
31400            if e.by_ref.is_some() {
31401                self.write_newline();
31402                self.write_indent();
31403                self.write_keyword("RETURNING SEQUENCE BY REF");
31404            }
31405
31406            if !e.columns.is_empty() {
31407                self.write_newline();
31408                self.write_indent();
31409                self.write_keyword("COLUMNS");
31410                for (i, col) in e.columns.iter().enumerate() {
31411                    self.write_newline();
31412                    self.indent_level += 1;
31413                    self.write_indent();
31414                    self.generate_expression(col)?;
31415                    self.indent_level -= 1;
31416                    if i < e.columns.len() - 1 {
31417                        self.write(",");
31418                    }
31419                }
31420            }
31421
31422            self.indent_level -= 1;
31423            self.write_newline();
31424            self.write_indent();
31425            self.write(")");
31426            return Ok(());
31427        }
31428
31429        // Namespaces - unwrap Tuple to generate comma-separated list without parentheses
31430        if let Some(namespaces) = &e.namespaces {
31431            self.write_keyword("XMLNAMESPACES");
31432            self.write("(");
31433            // Unwrap Tuple if present to avoid extra parentheses
31434            if let Expression::Tuple(tuple) = namespaces.as_ref() {
31435                for (i, expr) in tuple.expressions.iter().enumerate() {
31436                    if i > 0 {
31437                        self.write(", ");
31438                    }
31439                    // Python pattern: if it's an Alias, output as-is; otherwise prepend DEFAULT
31440                    // See xmlnamespace_sql in generator.py
31441                    if !matches!(expr, Expression::Alias(_)) {
31442                        self.write_keyword("DEFAULT");
31443                        self.write_space();
31444                    }
31445                    self.generate_expression(expr)?;
31446                }
31447            } else {
31448                // Single namespace - check if DEFAULT
31449                if !matches!(namespaces.as_ref(), Expression::Alias(_)) {
31450                    self.write_keyword("DEFAULT");
31451                    self.write_space();
31452                }
31453                self.generate_expression(namespaces)?;
31454            }
31455            self.write("), ");
31456        }
31457
31458        // XPath expression
31459        self.generate_expression(&e.this)?;
31460
31461        // PASSING clause - unwrap Tuple to generate comma-separated list without parentheses
31462        if let Some(passing) = &e.passing {
31463            self.write_space();
31464            self.write_keyword("PASSING");
31465            self.write_space();
31466            // Unwrap Tuple if present to avoid extra parentheses
31467            if let Expression::Tuple(tuple) = passing.as_ref() {
31468                for (i, expr) in tuple.expressions.iter().enumerate() {
31469                    if i > 0 {
31470                        self.write(", ");
31471                    }
31472                    self.generate_expression(expr)?;
31473                }
31474            } else {
31475                self.generate_expression(passing)?;
31476            }
31477        }
31478
31479        // RETURNING SEQUENCE BY REF
31480        if e.by_ref.is_some() {
31481            self.write_space();
31482            self.write_keyword("RETURNING SEQUENCE BY REF");
31483        }
31484
31485        // COLUMNS clause
31486        if !e.columns.is_empty() {
31487            self.write_space();
31488            self.write_keyword("COLUMNS");
31489            self.write_space();
31490            for (i, col) in e.columns.iter().enumerate() {
31491                if i > 0 {
31492                    self.write(", ");
31493                }
31494                self.generate_expression(col)?;
31495            }
31496        }
31497
31498        self.write(")");
31499        Ok(())
31500    }
31501
31502    fn generate_xor(&mut self, e: &Xor) -> Result<()> {
31503        // Python: return self.connector_sql(expression, "XOR", stack)
31504        // Handles: this XOR expression or expressions joined by XOR
31505        if let Some(this) = &e.this {
31506            self.generate_expression(this)?;
31507            if let Some(expression) = &e.expression {
31508                self.write_space();
31509                self.write_keyword("XOR");
31510                self.write_space();
31511                self.generate_expression(expression)?;
31512            }
31513        }
31514
31515        // Handle multiple expressions
31516        for (i, expr) in e.expressions.iter().enumerate() {
31517            if i > 0 || e.this.is_some() {
31518                self.write_space();
31519                self.write_keyword("XOR");
31520                self.write_space();
31521            }
31522            self.generate_expression(expr)?;
31523        }
31524        Ok(())
31525    }
31526
31527    fn generate_zipf(&mut self, e: &Zipf) -> Result<()> {
31528        // ZIPF(this, elementcount [, gen])
31529        self.write_keyword("ZIPF");
31530        self.write("(");
31531        self.generate_expression(&e.this)?;
31532        if let Some(elementcount) = &e.elementcount {
31533            self.write(", ");
31534            self.generate_expression(elementcount)?;
31535        }
31536        if let Some(gen) = &e.gen {
31537            self.write(", ");
31538            self.generate_expression(gen)?;
31539        }
31540        self.write(")");
31541        Ok(())
31542    }
31543}
31544
31545impl Default for Generator {
31546    fn default() -> Self {
31547        Self::new()
31548    }
31549}
31550
31551#[cfg(test)]
31552mod tests {
31553    use super::*;
31554    use crate::parser::Parser;
31555
31556    fn roundtrip(sql: &str) -> String {
31557        let ast = Parser::parse_sql(sql).unwrap();
31558        Generator::sql(&ast[0]).unwrap()
31559    }
31560
31561    #[test]
31562    fn test_simple_select() {
31563        let result = roundtrip("SELECT 1");
31564        assert_eq!(result, "SELECT 1");
31565    }
31566
31567    #[test]
31568    fn test_select_from() {
31569        let result = roundtrip("SELECT a, b FROM t");
31570        assert_eq!(result, "SELECT a, b FROM t");
31571    }
31572
31573    #[test]
31574    fn test_select_where() {
31575        let result = roundtrip("SELECT * FROM t WHERE x = 1");
31576        assert_eq!(result, "SELECT * FROM t WHERE x = 1");
31577    }
31578
31579    #[test]
31580    fn test_select_join() {
31581        let result = roundtrip("SELECT * FROM a JOIN b ON a.id = b.id");
31582        assert_eq!(result, "SELECT * FROM a JOIN b ON a.id = b.id");
31583    }
31584
31585    #[test]
31586    fn test_insert() {
31587        let result = roundtrip("INSERT INTO t (a, b) VALUES (1, 2)");
31588        assert_eq!(result, "INSERT INTO t (a, b) VALUES (1, 2)");
31589    }
31590
31591    #[test]
31592    fn test_pretty_print() {
31593        let ast = Parser::parse_sql("SELECT a, b FROM t WHERE x = 1").unwrap();
31594        let result = Generator::pretty_sql(&ast[0]).unwrap();
31595        assert!(result.contains('\n'));
31596    }
31597
31598    #[test]
31599    fn test_window_function() {
31600        let result = roundtrip("SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)");
31601        assert_eq!(result, "SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)");
31602    }
31603
31604    #[test]
31605    fn test_window_function_with_frame() {
31606        let result = roundtrip("SELECT SUM(amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)");
31607        assert_eq!(result, "SELECT SUM(amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)");
31608    }
31609
31610    #[test]
31611    fn test_aggregate_with_filter() {
31612        let result = roundtrip("SELECT COUNT(*) FILTER (WHERE status = 1) FROM orders");
31613        assert_eq!(result, "SELECT COUNT(*) FILTER(WHERE status = 1) FROM orders");
31614    }
31615
31616    #[test]
31617    fn test_subscript() {
31618        let result = roundtrip("SELECT arr[0]");
31619        assert_eq!(result, "SELECT arr[0]");
31620    }
31621
31622    // DDL tests
31623    #[test]
31624    fn test_create_table() {
31625        let result = roundtrip("CREATE TABLE users (id INT, name VARCHAR(100))");
31626        assert_eq!(result, "CREATE TABLE users (id INT, name VARCHAR(100))");
31627    }
31628
31629    #[test]
31630    fn test_create_table_with_constraints() {
31631        let result = roundtrip("CREATE TABLE users (id INT PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL)");
31632        assert_eq!(result, "CREATE TABLE users (id INT PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL)");
31633    }
31634
31635    #[test]
31636    fn test_create_table_if_not_exists() {
31637        let result = roundtrip("CREATE TABLE IF NOT EXISTS t (id INT)");
31638        assert_eq!(result, "CREATE TABLE IF NOT EXISTS t (id INT)");
31639    }
31640
31641    #[test]
31642    fn test_drop_table() {
31643        let result = roundtrip("DROP TABLE users");
31644        assert_eq!(result, "DROP TABLE users");
31645    }
31646
31647    #[test]
31648    fn test_drop_table_if_exists_cascade() {
31649        let result = roundtrip("DROP TABLE IF EXISTS users CASCADE");
31650        assert_eq!(result, "DROP TABLE IF EXISTS users CASCADE");
31651    }
31652
31653    #[test]
31654    fn test_alter_table_add_column() {
31655        let result = roundtrip("ALTER TABLE users ADD COLUMN email VARCHAR(255)");
31656        assert_eq!(result, "ALTER TABLE users ADD COLUMN email VARCHAR(255)");
31657    }
31658
31659    #[test]
31660    fn test_alter_table_drop_column() {
31661        let result = roundtrip("ALTER TABLE users DROP COLUMN email");
31662        assert_eq!(result, "ALTER TABLE users DROP COLUMN email");
31663    }
31664
31665    #[test]
31666    fn test_create_index() {
31667        let result = roundtrip("CREATE INDEX idx_name ON users(name)");
31668        assert_eq!(result, "CREATE INDEX idx_name ON users(name)");
31669    }
31670
31671    #[test]
31672    fn test_create_unique_index() {
31673        let result = roundtrip("CREATE UNIQUE INDEX idx_email ON users(email)");
31674        assert_eq!(result, "CREATE UNIQUE INDEX idx_email ON users(email)");
31675    }
31676
31677    #[test]
31678    fn test_drop_index() {
31679        let result = roundtrip("DROP INDEX idx_name");
31680        assert_eq!(result, "DROP INDEX idx_name");
31681    }
31682
31683    #[test]
31684    fn test_create_view() {
31685        let result = roundtrip("CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1");
31686        assert_eq!(result, "CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1");
31687    }
31688
31689    #[test]
31690    fn test_drop_view() {
31691        let result = roundtrip("DROP VIEW active_users");
31692        assert_eq!(result, "DROP VIEW active_users");
31693    }
31694
31695    #[test]
31696    fn test_truncate() {
31697        let result = roundtrip("TRUNCATE TABLE users");
31698        assert_eq!(result, "TRUNCATE TABLE users");
31699    }
31700
31701    #[test]
31702    fn test_string_literal_escaping_default() {
31703        // Default: double single quotes
31704        let result = roundtrip("SELECT 'hello'");
31705        assert_eq!(result, "SELECT 'hello'");
31706
31707        // Single quotes are doubled
31708        let result = roundtrip("SELECT 'it''s a test'");
31709        assert_eq!(result, "SELECT 'it''s a test'");
31710    }
31711
31712    #[test]
31713    fn test_string_literal_escaping_mysql() {
31714        use crate::dialects::DialectType;
31715
31716        let config = GeneratorConfig {
31717            dialect: Some(DialectType::MySQL),
31718            ..Default::default()
31719        };
31720
31721        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31722        let mut gen = Generator::with_config(config.clone());
31723        let result = gen.generate(&ast[0]).unwrap();
31724        assert_eq!(result, "SELECT 'hello'");
31725
31726        // MySQL uses SQL standard quote doubling for escaping (matches Python sqlglot)
31727        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31728        let mut gen = Generator::with_config(config.clone());
31729        let result = gen.generate(&ast[0]).unwrap();
31730        assert_eq!(result, "SELECT 'it''s'");
31731    }
31732
31733    #[test]
31734    fn test_string_literal_escaping_postgres() {
31735        use crate::dialects::DialectType;
31736
31737        let config = GeneratorConfig {
31738            dialect: Some(DialectType::PostgreSQL),
31739            ..Default::default()
31740        };
31741
31742        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31743        let mut gen = Generator::with_config(config.clone());
31744        let result = gen.generate(&ast[0]).unwrap();
31745        assert_eq!(result, "SELECT 'hello'");
31746
31747        // PostgreSQL uses doubled quotes for regular strings
31748        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31749        let mut gen = Generator::with_config(config.clone());
31750        let result = gen.generate(&ast[0]).unwrap();
31751        assert_eq!(result, "SELECT 'it''s'");
31752    }
31753
31754    #[test]
31755    fn test_string_literal_escaping_bigquery() {
31756        use crate::dialects::DialectType;
31757
31758        let config = GeneratorConfig {
31759            dialect: Some(DialectType::BigQuery),
31760            ..Default::default()
31761        };
31762
31763        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31764        let mut gen = Generator::with_config(config.clone());
31765        let result = gen.generate(&ast[0]).unwrap();
31766        assert_eq!(result, "SELECT 'hello'");
31767
31768        // BigQuery escapes single quotes with backslash
31769        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31770        let mut gen = Generator::with_config(config.clone());
31771        let result = gen.generate(&ast[0]).unwrap();
31772        assert_eq!(result, "SELECT 'it\\'s'");
31773    }
31774
31775}