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::Custom { name } => {
19348                // Handle dialect-specific type transformations
19349                let name_upper = name.to_uppercase();
19350                match self.config.dialect {
19351                    Some(DialectType::ClickHouse) => {
19352                        let (base_upper, suffix) = if let Some(idx) = name.find('(') {
19353                            (name_upper[..idx].to_string(), &name[idx..])
19354                        } else {
19355                            (name_upper.clone(), "")
19356                        };
19357                        let mapped = match base_upper.as_str() {
19358                            "DATETIME" | "TIMESTAMPTZ" | "TIMESTAMP" | "TIMESTAMPNTZ" | "SMALLDATETIME" | "DATETIME2" => "DateTime",
19359                            "DATETIME64" => "DateTime64",
19360                            "DATE32" => "Date32",
19361                            "INT" => "Int32",
19362                            "MEDIUMINT" => "Int32",
19363                            "INT8" => "Int8",
19364                            "INT16" => "Int16",
19365                            "INT32" => "Int32",
19366                            "INT64" => "Int64",
19367                            "INT128" => "Int128",
19368                            "INT256" => "Int256",
19369                            "UINT8" => "UInt8",
19370                            "UINT16" => "UInt16",
19371                            "UINT32" => "UInt32",
19372                            "UINT64" => "UInt64",
19373                            "UINT128" => "UInt128",
19374                            "UINT256" => "UInt256",
19375                            "FLOAT32" => "Float32",
19376                            "FLOAT64" => "Float64",
19377                            "DECIMAL32" => "Decimal32",
19378                            "DECIMAL64" => "Decimal64",
19379                            "DECIMAL128" => "Decimal128",
19380                            "DECIMAL256" => "Decimal256",
19381                            "ENUM" => "Enum",
19382                            "ENUM8" => "Enum8",
19383                            "ENUM16" => "Enum16",
19384                            "FIXEDSTRING" => "FixedString",
19385                            "NESTED" => "Nested",
19386                            "LOWCARDINALITY" => "LowCardinality",
19387                            "NULLABLE" => "Nullable",
19388                            "IPV4" => "IPv4",
19389                            "IPV6" => "IPv6",
19390                            "POINT" => "Point",
19391                            "RING" => "Ring",
19392                            "LINESTRING" => "LineString",
19393                            "MULTILINESTRING" => "MultiLineString",
19394                            "POLYGON" => "Polygon",
19395                            "MULTIPOLYGON" => "MultiPolygon",
19396                            "AGGREGATEFUNCTION" => "AggregateFunction",
19397                            "SIMPLEAGGREGATEFUNCTION" => "SimpleAggregateFunction",
19398                            "DYNAMIC" => "Dynamic",
19399                            _ => "",
19400                        };
19401                        if mapped.is_empty() {
19402                            self.write(name);
19403                        } else {
19404                            self.write(mapped);
19405                            self.write(suffix);
19406                        }
19407                    }
19408                    Some(DialectType::MySQL) if name_upper == "TIMESTAMPTZ" || name_upper == "TIMESTAMPLTZ" => {
19409                        // MySQL doesn't support TIMESTAMPTZ/TIMESTAMPLTZ, use TIMESTAMP
19410                        self.write_keyword("TIMESTAMP");
19411                    }
19412                    Some(DialectType::TSQL) if name_upper == "VARIANT" => {
19413                        self.write_keyword("SQL_VARIANT");
19414                    }
19415                    Some(DialectType::DuckDB) if name_upper == "DECFLOAT" => {
19416                        self.write_keyword("DECIMAL(38, 5)");
19417                    }
19418                    Some(DialectType::Exasol) => {
19419                        // Exasol type mappings for custom types
19420                        match name_upper.as_str() {
19421                            // Binary types → VARCHAR
19422                            "LONGBLOB" | "MEDIUMBLOB" | "TINYBLOB" => self.write_keyword("VARCHAR"),
19423                            // Text types → VARCHAR (TEXT → LONG VARCHAR is handled by DataType::Text)
19424                            "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" => self.write_keyword("VARCHAR"),
19425                            // Integer types
19426                            "MEDIUMINT" => self.write_keyword("INT"),
19427                            // Decimal types → DECIMAL
19428                            "DECIMAL32" | "DECIMAL64" | "DECIMAL128" | "DECIMAL256" => self.write_keyword("DECIMAL"),
19429                            // Timestamp types
19430                            "DATETIME" => self.write_keyword("TIMESTAMP"),
19431                            "TIMESTAMPLTZ" => self.write_keyword("TIMESTAMP WITH LOCAL TIME ZONE"),
19432                            _ => self.write(name),
19433                        }
19434                    }
19435                    Some(DialectType::Dremio) => {
19436                        // Dremio type mappings for custom types
19437                        match name_upper.as_str() {
19438                            "TIMESTAMPNTZ" | "DATETIME" => self.write_keyword("TIMESTAMP"),
19439                            "ARRAY" => self.write_keyword("LIST"),
19440                            "NCHAR" => self.write_keyword("VARCHAR"),
19441                            _ => self.write(name),
19442                        }
19443                    }
19444                    // Map dialect-specific custom types to standard SQL types for other dialects
19445                    _ => {
19446                        // Extract base name and args for types with parenthesized args (e.g., DATETIME2(3))
19447                        let (base_upper, _args_str) = if let Some(idx) = name_upper.find('(') {
19448                            (name_upper[..idx].to_string(), Some(&name[idx..]))
19449                        } else {
19450                            (name_upper.clone(), None)
19451                        };
19452
19453                        match base_upper.as_str() {
19454                            "INT64" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19455                                self.write_keyword("BIGINT");
19456                            }
19457                            "FLOAT64" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19458                                self.write_keyword("DOUBLE");
19459                            }
19460                            "BOOL" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19461                                self.write_keyword("BOOLEAN");
19462                            }
19463                            "BYTES" if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)) => {
19464                                self.write_keyword("BINARY");
19465                            }
19466                            "BYTES" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19467                                self.write_keyword("VARBINARY");
19468                            }
19469                            // TSQL DATETIME2/SMALLDATETIME -> TIMESTAMP
19470                            "DATETIME2" | "SMALLDATETIME" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19471                                // PostgreSQL preserves precision, others don't
19472                                if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
19473                                    self.write_keyword("TIMESTAMP");
19474                                    if let Some(args) = _args_str {
19475                                        self.write(args);
19476                                    }
19477                                } else {
19478                                    self.write_keyword("TIMESTAMP");
19479                                }
19480                            }
19481                            // TSQL DATETIMEOFFSET -> TIMESTAMPTZ
19482                            "DATETIMEOFFSET" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19483                                if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
19484                                    self.write_keyword("TIMESTAMPTZ");
19485                                    if let Some(args) = _args_str {
19486                                        self.write(args);
19487                                    }
19488                                } else {
19489                                    self.write_keyword("TIMESTAMPTZ");
19490                                }
19491                            }
19492                            // TSQL UNIQUEIDENTIFIER -> UUID or STRING
19493                            "UNIQUEIDENTIFIER" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19494                                match self.config.dialect {
19495                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19496                                    | Some(DialectType::Hive) => self.write_keyword("STRING"),
19497                                    _ => self.write_keyword("UUID"),
19498                                }
19499                            }
19500                            // TSQL BIT -> BOOLEAN for most dialects
19501                            "BIT" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)
19502                                | Some(DialectType::PostgreSQL) | Some(DialectType::MySQL) | Some(DialectType::DuckDB)) => {
19503                                self.write_keyword("BOOLEAN");
19504                            }
19505                            // TSQL NVARCHAR -> VARCHAR (with default size 30 for some dialects)
19506                            "NVARCHAR" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19507                                match self.config.dialect {
19508                                    Some(DialectType::SQLite) => {
19509                                        self.write_keyword("TEXT");
19510                                        if let Some(args) = _args_str {
19511                                            self.write(args);
19512                                        }
19513                                    }
19514                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19515                                    | Some(DialectType::Hive) => {
19516                                        if _args_str.is_some() {
19517                                            self.write_keyword("VARCHAR");
19518                                            self.write(_args_str.unwrap());
19519                                        } else {
19520                                            self.write_keyword("VARCHAR(30)");
19521                                        }
19522                                    }
19523                                    _ => {
19524                                        self.write_keyword("VARCHAR");
19525                                        if let Some(args) = _args_str {
19526                                            self.write(args);
19527                                        }
19528                                    }
19529                                }
19530                            }
19531                            // TSQL NCHAR -> CHAR
19532                            "NCHAR" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19533                                match self.config.dialect {
19534                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19535                                    | Some(DialectType::Hive) => {
19536                                        if _args_str.is_some() {
19537                                            self.write_keyword("CHAR");
19538                                            self.write(_args_str.unwrap());
19539                                        } else {
19540                                            self.write_keyword("CHAR(30)");
19541                                        }
19542                                    }
19543                                    _ => {
19544                                        self.write_keyword("CHAR");
19545                                        if let Some(args) = _args_str {
19546                                            self.write(args);
19547                                        }
19548                                    }
19549                                }
19550                            }
19551                            // MySQL text variant types -> map to appropriate target type
19552                            // For MySQL/SingleStore: keep original name (column definitions), CAST handling is in generate_cast
19553                            "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" => {
19554                                match self.config.dialect {
19555                                    Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB) => self.write_keyword(&base_upper),
19556                                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => self.write_keyword("TEXT"),
19557                                    Some(DialectType::BigQuery) => self.write_keyword("STRING"),
19558                                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARCHAR"),
19559                                    Some(DialectType::Snowflake) | Some(DialectType::Redshift) | Some(DialectType::Dremio) => self.write_keyword("VARCHAR"),
19560                                    _ => self.write_keyword("TEXT"),
19561                                }
19562                            }
19563                            // MySQL blob variant types -> map to appropriate target type
19564                            // For MySQL/SingleStore: keep original name (column definitions), CAST handling is in generate_cast
19565                            "LONGBLOB" | "MEDIUMBLOB" | "TINYBLOB" => {
19566                                match self.config.dialect {
19567                                    Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB) => self.write_keyword(&base_upper),
19568                                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => self.write_keyword("BLOB"),
19569                                    Some(DialectType::DuckDB) => self.write_keyword("VARBINARY"),
19570                                    Some(DialectType::BigQuery) => self.write_keyword("BYTES"),
19571                                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARBINARY"),
19572                                    Some(DialectType::Snowflake) | Some(DialectType::Redshift) | Some(DialectType::Dremio) => self.write_keyword("VARBINARY"),
19573                                    _ => self.write_keyword("BLOB"),
19574                                }
19575                            }
19576                            // LONGVARCHAR -> TEXT for SQLite, VARCHAR for others
19577                            "LONGVARCHAR" => {
19578                                match self.config.dialect {
19579                                    Some(DialectType::SQLite) => self.write_keyword("TEXT"),
19580                                    _ => self.write_keyword("VARCHAR"),
19581                                }
19582                            }
19583                            _ => self.write(name),
19584                        }
19585                    }
19586                }
19587            }
19588            DataType::Geometry { subtype, srid } => {
19589                // Dialect-specific geometry type mappings
19590                match self.config.dialect {
19591                    Some(DialectType::MySQL) => {
19592                        // MySQL uses POINT SRID 4326 syntax for specific types
19593                        if let Some(sub) = subtype {
19594                            self.write_keyword(sub);
19595                            if let Some(s) = srid {
19596                                self.write(" SRID ");
19597                                self.write(&s.to_string());
19598                            }
19599                        } else {
19600                            self.write_keyword("GEOMETRY");
19601                        }
19602                    }
19603                    Some(DialectType::BigQuery) => {
19604                        // BigQuery only supports GEOGRAPHY, not GEOMETRY
19605                        self.write_keyword("GEOGRAPHY");
19606                    }
19607                    Some(DialectType::Teradata) => {
19608                        // Teradata uses ST_GEOMETRY
19609                        self.write_keyword("ST_GEOMETRY");
19610                        if subtype.is_some() || srid.is_some() {
19611                            self.write("(");
19612                            if let Some(sub) = subtype {
19613                                self.write_keyword(sub);
19614                            }
19615                            if let Some(s) = srid {
19616                                if subtype.is_some() {
19617                                    self.write(", ");
19618                                }
19619                                self.write(&s.to_string());
19620                            }
19621                            self.write(")");
19622                        }
19623                    }
19624                    _ => {
19625                        // PostgreSQL, Snowflake, DuckDB use GEOMETRY(subtype, srid) syntax
19626                        self.write_keyword("GEOMETRY");
19627                        if subtype.is_some() || srid.is_some() {
19628                            self.write("(");
19629                            if let Some(sub) = subtype {
19630                                self.write_keyword(sub);
19631                            }
19632                            if let Some(s) = srid {
19633                                if subtype.is_some() {
19634                                    self.write(", ");
19635                                }
19636                                self.write(&s.to_string());
19637                            }
19638                            self.write(")");
19639                        }
19640                    }
19641                }
19642            }
19643            DataType::Geography { subtype, srid } => {
19644                // Dialect-specific geography type mappings
19645                match self.config.dialect {
19646                    Some(DialectType::MySQL) => {
19647                        // MySQL doesn't have native GEOGRAPHY, use GEOMETRY with SRID 4326
19648                        if let Some(sub) = subtype {
19649                            self.write_keyword(sub);
19650                        } else {
19651                            self.write_keyword("GEOMETRY");
19652                        }
19653                        // Geography implies SRID 4326 (WGS84)
19654                        let effective_srid = srid.unwrap_or(4326);
19655                        self.write(" SRID ");
19656                        self.write(&effective_srid.to_string());
19657                    }
19658                    Some(DialectType::BigQuery) => {
19659                        // BigQuery uses simple GEOGRAPHY without parameters
19660                        self.write_keyword("GEOGRAPHY");
19661                    }
19662                    Some(DialectType::Snowflake) => {
19663                        // Snowflake uses GEOGRAPHY without parameters
19664                        self.write_keyword("GEOGRAPHY");
19665                    }
19666                    _ => {
19667                        // PostgreSQL uses GEOGRAPHY(subtype, srid) syntax
19668                        self.write_keyword("GEOGRAPHY");
19669                        if subtype.is_some() || srid.is_some() {
19670                            self.write("(");
19671                            if let Some(sub) = subtype {
19672                                self.write_keyword(sub);
19673                            }
19674                            if let Some(s) = srid {
19675                                if subtype.is_some() {
19676                                    self.write(", ");
19677                                }
19678                                self.write(&s.to_string());
19679                            }
19680                            self.write(")");
19681                        }
19682                    }
19683                }
19684            }
19685            DataType::CharacterSet { name } => {
19686                // For MySQL CONVERT USING - output as CHAR CHARACTER SET name
19687                self.write_keyword("CHAR CHARACTER SET ");
19688                self.write(name);
19689            }
19690            _ => self.write("UNKNOWN"),
19691        }
19692        Ok(())
19693    }
19694
19695    // === Helper methods ===
19696
19697    fn write(&mut self, s: &str) {
19698        self.output.push_str(s);
19699    }
19700
19701    fn write_space(&mut self) {
19702        self.output.push(' ');
19703    }
19704
19705    fn write_keyword(&mut self, keyword: &str) {
19706        if self.config.uppercase_keywords {
19707            self.output.push_str(keyword);
19708        } else {
19709            self.output.push_str(&keyword.to_lowercase());
19710        }
19711    }
19712
19713    /// Convert strptime format string to Exasol format string
19714    /// Exasol TIME_MAPPING (reverse of Python sqlglot):
19715    /// %Y -> YYYY, %y -> YY, %m -> MM, %d -> DD, %H -> HH, %M -> MI, %S -> SS, %a -> DY
19716    fn convert_strptime_to_exasol_format(format: &str) -> String {
19717        let mut result = String::new();
19718        let chars: Vec<char> = format.chars().collect();
19719        let mut i = 0;
19720        while i < chars.len() {
19721            if chars[i] == '%' && i + 1 < chars.len() {
19722                let spec = chars[i + 1];
19723                let exasol_spec = match spec {
19724                    'Y' => "YYYY",
19725                    'y' => "YY",
19726                    'm' => "MM",
19727                    'd' => "DD",
19728                    'H' => "HH",
19729                    'M' => "MI",
19730                    'S' => "SS",
19731                    'a' => "DY",   // abbreviated weekday name
19732                    'A' => "DAY",  // full weekday name
19733                    'b' => "MON",  // abbreviated month name
19734                    'B' => "MONTH", // full month name
19735                    'I' => "H12",  // 12-hour format
19736                    'u' => "ID",   // ISO weekday (1-7)
19737                    'V' => "IW",   // ISO week number
19738                    'G' => "IYYY", // ISO year
19739                    'W' => "UW",   // Week number (Monday as first day)
19740                    'U' => "UW",   // Week number (Sunday as first day)
19741                    'z' => "Z",    // timezone offset
19742                    _ => {
19743                        // Unknown specifier, keep as-is
19744                        result.push('%');
19745                        result.push(spec);
19746                        i += 2;
19747                        continue;
19748                    }
19749                };
19750                result.push_str(exasol_spec);
19751                i += 2;
19752            } else {
19753                result.push(chars[i]);
19754                i += 1;
19755            }
19756        }
19757        result
19758    }
19759
19760    /// Convert strptime format string to PostgreSQL/Redshift format string
19761    /// PostgreSQL INVERSE_TIME_MAPPING from Python sqlglot:
19762    /// %Y -> YYYY, %y -> YY, %m -> MM, %d -> DD, %H -> HH24, %M -> MI, %S -> SS, %f -> US, etc.
19763    fn convert_strptime_to_postgres_format(format: &str) -> String {
19764        let mut result = String::new();
19765        let chars: Vec<char> = format.chars().collect();
19766        let mut i = 0;
19767        while i < chars.len() {
19768            if chars[i] == '%' && i + 1 < chars.len() {
19769                let spec = chars[i + 1];
19770                let pg_spec = match spec {
19771                    'Y' => "YYYY",
19772                    'y' => "YY",
19773                    'm' => "MM",
19774                    'd' => "DD",
19775                    'H' => "HH24",
19776                    'I' => "HH12",
19777                    'M' => "MI",
19778                    'S' => "SS",
19779                    'f' => "US",     // microseconds
19780                    'u' => "D",      // day of week (1=Monday)
19781                    'j' => "DDD",    // day of year
19782                    'z' => "OF",     // UTC offset
19783                    'Z' => "TZ",     // timezone name
19784                    'A' => "TMDay",  // full weekday name
19785                    'a' => "TMDy",   // abbreviated weekday name
19786                    'b' => "TMMon",  // abbreviated month name
19787                    'B' => "TMMonth", // full month name
19788                    'U' => "WW",     // week number
19789                    _ => {
19790                        // Unknown specifier, keep as-is
19791                        result.push('%');
19792                        result.push(spec);
19793                        i += 2;
19794                        continue;
19795                    }
19796                };
19797                result.push_str(pg_spec);
19798                i += 2;
19799            } else {
19800                result.push(chars[i]);
19801                i += 1;
19802            }
19803        }
19804        result
19805    }
19806
19807    /// Write a LIMIT expression value, evaluating constant expressions if limit_only_literals is set
19808    fn write_limit_expr(&mut self, expr: &Expression) -> Result<()> {
19809        if self.config.limit_only_literals {
19810            if let Some(value) = Self::try_evaluate_constant(expr) {
19811                self.write(&value.to_string());
19812                return Ok(());
19813            }
19814        }
19815        self.generate_expression(expr)
19816    }
19817
19818    /// Format a comment with proper spacing.
19819    /// Converts `/*text*/` to `/* text */` (adding internal spaces if not present).
19820    /// Python SQLGlot normalizes comment format to have spaces inside block comments.
19821    fn write_formatted_comment(&mut self, comment: &str) {
19822        // Normalize all comments to block comment format /* ... */
19823        // This matches Python sqlglot behavior which always outputs block comments
19824        let content = if comment.starts_with("/*") && comment.ends_with("*/") {
19825            // Already block comment - extract inner content
19826            comment[2..comment.len() - 2].trim()
19827        } else if comment.starts_with("--") {
19828            // Line comment - extract content after --
19829            comment[2..].trim()
19830        } else {
19831            // Raw content (no delimiters)
19832            comment.trim()
19833        };
19834        self.output.push_str("/* ");
19835        self.output.push_str(content);
19836        self.output.push_str(" */");
19837    }
19838
19839    /// Escape a raw block content (from dollar-quoted string) for single-quoted output.
19840    /// Escapes single quotes with backslash, and for Snowflake also escapes backslashes.
19841    fn escape_block_for_single_quote(&self, block: &str) -> String {
19842        let escape_backslash = matches!(
19843            self.config.dialect,
19844            Some(crate::dialects::DialectType::Snowflake)
19845        );
19846        let mut escaped = String::with_capacity(block.len() + 4);
19847        for ch in block.chars() {
19848            if ch == '\'' {
19849                escaped.push('\\');
19850                escaped.push('\'');
19851            } else if escape_backslash && ch == '\\' {
19852                escaped.push('\\');
19853                escaped.push('\\');
19854            } else {
19855                escaped.push(ch);
19856            }
19857        }
19858        escaped
19859    }
19860
19861    fn write_newline(&mut self) {
19862        self.output.push('\n');
19863    }
19864
19865    fn write_indent(&mut self) {
19866        for _ in 0..self.indent_level {
19867            self.output.push_str(&self.config.indent);
19868        }
19869    }
19870
19871    // === SQLGlot-style pretty printing helpers ===
19872
19873    /// Returns the separator string for pretty printing.
19874    /// Check if the total length of arguments exceeds max_text_width.
19875    /// Used for dynamic line breaking in expressions() formatting.
19876    fn too_wide(&self, args: &[String]) -> bool {
19877        args.iter().map(|s| s.len()).sum::<usize>() > self.config.max_text_width
19878    }
19879
19880    /// Writes a clause with a single condition (WHERE, HAVING, QUALIFY).
19881    /// In pretty mode: newline + indented keyword + newline + indented condition
19882    fn write_clause_condition(&mut self, keyword: &str, condition: &Expression) -> Result<()> {
19883        if self.config.pretty {
19884            self.write_newline();
19885            self.write_indent();
19886            self.write_keyword(keyword);
19887            self.write_newline();
19888            self.indent_level += 1;
19889            self.write_indent();
19890            self.generate_expression(condition)?;
19891            self.indent_level -= 1;
19892        } else {
19893            self.write_space();
19894            self.write_keyword(keyword);
19895            self.write_space();
19896            self.generate_expression(condition)?;
19897        }
19898        Ok(())
19899    }
19900
19901    /// Writes a clause with a list of expressions (GROUP BY, DISTRIBUTE BY, CLUSTER BY).
19902    /// In pretty mode: each expression on new line with indentation
19903    fn write_clause_expressions(&mut self, keyword: &str, exprs: &[Expression]) -> Result<()> {
19904        if exprs.is_empty() {
19905            return Ok(());
19906        }
19907
19908        if self.config.pretty {
19909            self.write_newline();
19910            self.write_indent();
19911            self.write_keyword(keyword);
19912            self.write_newline();
19913            self.indent_level += 1;
19914            for (i, expr) in exprs.iter().enumerate() {
19915                if i > 0 {
19916                    self.write(",");
19917                    self.write_newline();
19918                }
19919                self.write_indent();
19920                self.generate_expression(expr)?;
19921            }
19922            self.indent_level -= 1;
19923        } else {
19924            self.write_space();
19925            self.write_keyword(keyword);
19926            self.write_space();
19927            for (i, expr) in exprs.iter().enumerate() {
19928                if i > 0 {
19929                    self.write(", ");
19930                }
19931                self.generate_expression(expr)?;
19932            }
19933        }
19934        Ok(())
19935    }
19936
19937    /// Writes ORDER BY / SORT BY clause with Ordered expressions
19938    fn write_order_clause(&mut self, keyword: &str, orderings: &[Ordered]) -> Result<()> {
19939        if orderings.is_empty() {
19940            return Ok(());
19941        }
19942
19943        if self.config.pretty {
19944            self.write_newline();
19945            self.write_indent();
19946            self.write_keyword(keyword);
19947            self.write_newline();
19948            self.indent_level += 1;
19949            for (i, ordered) in orderings.iter().enumerate() {
19950                if i > 0 {
19951                    self.write(",");
19952                    self.write_newline();
19953                }
19954                self.write_indent();
19955                self.generate_ordered(ordered)?;
19956            }
19957            self.indent_level -= 1;
19958        } else {
19959            self.write_space();
19960            self.write_keyword(keyword);
19961            self.write_space();
19962            for (i, ordered) in orderings.iter().enumerate() {
19963                if i > 0 {
19964                    self.write(", ");
19965                }
19966                self.generate_ordered(ordered)?;
19967            }
19968        }
19969        Ok(())
19970    }
19971
19972    /// Writes WINDOW clause with named window definitions
19973    fn write_window_clause(&mut self, windows: &[NamedWindow]) -> Result<()> {
19974        if windows.is_empty() {
19975            return Ok(());
19976        }
19977
19978        if self.config.pretty {
19979            self.write_newline();
19980            self.write_indent();
19981            self.write_keyword("WINDOW");
19982            self.write_newline();
19983            self.indent_level += 1;
19984            for (i, named_window) in windows.iter().enumerate() {
19985                if i > 0 {
19986                    self.write(",");
19987                    self.write_newline();
19988                }
19989                self.write_indent();
19990                self.generate_identifier(&named_window.name)?;
19991                self.write_space();
19992                self.write_keyword("AS");
19993                self.write(" (");
19994                self.generate_over(&named_window.spec)?;
19995                self.write(")");
19996            }
19997            self.indent_level -= 1;
19998        } else {
19999            self.write_space();
20000            self.write_keyword("WINDOW");
20001            self.write_space();
20002            for (i, named_window) in windows.iter().enumerate() {
20003                if i > 0 {
20004                    self.write(", ");
20005                }
20006                self.generate_identifier(&named_window.name)?;
20007                self.write_space();
20008                self.write_keyword("AS");
20009                self.write(" (");
20010                self.generate_over(&named_window.spec)?;
20011                self.write(")");
20012            }
20013        }
20014        Ok(())
20015    }
20016
20017    // === BATCH-GENERATED STUB METHODS (481 variants) ===
20018    fn generate_ai_agg(&mut self, e: &AIAgg) -> Result<()> {
20019        // AI_AGG(this, expression)
20020        self.write_keyword("AI_AGG");
20021        self.write("(");
20022        self.generate_expression(&e.this)?;
20023        self.write(", ");
20024        self.generate_expression(&e.expression)?;
20025        self.write(")");
20026        Ok(())
20027    }
20028
20029    fn generate_ai_classify(&mut self, e: &AIClassify) -> Result<()> {
20030        // AI_CLASSIFY(input, [categories], [config])
20031        self.write_keyword("AI_CLASSIFY");
20032        self.write("(");
20033        self.generate_expression(&e.this)?;
20034        if let Some(categories) = &e.categories {
20035            self.write(", ");
20036            self.generate_expression(categories)?;
20037        }
20038        if let Some(config) = &e.config {
20039            self.write(", ");
20040            self.generate_expression(config)?;
20041        }
20042        self.write(")");
20043        Ok(())
20044    }
20045
20046    fn generate_add_partition(&mut self, e: &AddPartition) -> Result<()> {
20047        // Python: return f"ADD {exists}{self.sql(expression.this)}{location}"
20048        self.write_keyword("ADD");
20049        self.write_space();
20050        if e.exists {
20051            self.write_keyword("IF NOT EXISTS");
20052            self.write_space();
20053        }
20054        self.generate_expression(&e.this)?;
20055        if let Some(location) = &e.location {
20056            self.write_space();
20057            self.generate_expression(location)?;
20058        }
20059        Ok(())
20060    }
20061
20062    fn generate_algorithm_property(&mut self, e: &AlgorithmProperty) -> Result<()> {
20063        // Python: return f"ALGORITHM={self.sql(expression, 'this')}"
20064        self.write_keyword("ALGORITHM");
20065        self.write("=");
20066        self.generate_expression(&e.this)?;
20067        Ok(())
20068    }
20069
20070    fn generate_aliases(&mut self, e: &Aliases) -> Result<()> {
20071        // Python: return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
20072        self.generate_expression(&e.this)?;
20073        self.write_space();
20074        self.write_keyword("AS");
20075        self.write(" (");
20076        for (i, expr) in e.expressions.iter().enumerate() {
20077            if i > 0 {
20078                self.write(", ");
20079            }
20080            self.generate_expression(expr)?;
20081        }
20082        self.write(")");
20083        Ok(())
20084    }
20085
20086    fn generate_allowed_values_property(&mut self, e: &AllowedValuesProperty) -> Result<()> {
20087        // Python: return f"ALLOWED_VALUES {self.expressions(e, flat=True)}"
20088        self.write_keyword("ALLOWED_VALUES");
20089        self.write_space();
20090        for (i, expr) in e.expressions.iter().enumerate() {
20091            if i > 0 {
20092                self.write(", ");
20093            }
20094            self.generate_expression(expr)?;
20095        }
20096        Ok(())
20097    }
20098
20099    fn generate_alter_column(&mut self, e: &AlterColumn) -> Result<()> {
20100        // Python: complex logic based on dtype, default, comment, visible, etc.
20101        self.write_keyword("ALTER COLUMN");
20102        self.write_space();
20103        self.generate_expression(&e.this)?;
20104
20105        if let Some(dtype) = &e.dtype {
20106            self.write_space();
20107            self.write_keyword("SET DATA TYPE");
20108            self.write_space();
20109            self.generate_expression(dtype)?;
20110            if let Some(collate) = &e.collate {
20111                self.write_space();
20112                self.write_keyword("COLLATE");
20113                self.write_space();
20114                self.generate_expression(collate)?;
20115            }
20116            if let Some(using) = &e.using {
20117                self.write_space();
20118                self.write_keyword("USING");
20119                self.write_space();
20120                self.generate_expression(using)?;
20121            }
20122        } else if let Some(default) = &e.default {
20123            self.write_space();
20124            self.write_keyword("SET DEFAULT");
20125            self.write_space();
20126            self.generate_expression(default)?;
20127        } else if let Some(comment) = &e.comment {
20128            self.write_space();
20129            self.write_keyword("COMMENT");
20130            self.write_space();
20131            self.generate_expression(comment)?;
20132        } else if let Some(drop) = &e.drop {
20133            self.write_space();
20134            self.write_keyword("DROP");
20135            self.write_space();
20136            self.generate_expression(drop)?;
20137        } else if let Some(visible) = &e.visible {
20138            self.write_space();
20139            self.generate_expression(visible)?;
20140        } else if let Some(rename_to) = &e.rename_to {
20141            self.write_space();
20142            self.write_keyword("RENAME TO");
20143            self.write_space();
20144            self.generate_expression(rename_to)?;
20145        } else if let Some(allow_null) = &e.allow_null {
20146            self.write_space();
20147            self.generate_expression(allow_null)?;
20148        }
20149        Ok(())
20150    }
20151
20152    fn generate_alter_session(&mut self, e: &AlterSession) -> Result<()> {
20153        // Python: keyword = "UNSET" if expression.args.get("unset") else "SET"; return f"{keyword} {items_sql}"
20154        self.write_keyword("ALTER SESSION");
20155        self.write_space();
20156        if e.unset.is_some() {
20157            self.write_keyword("UNSET");
20158        } else {
20159            self.write_keyword("SET");
20160        }
20161        self.write_space();
20162        for (i, expr) in e.expressions.iter().enumerate() {
20163            if i > 0 {
20164                self.write(", ");
20165            }
20166            self.generate_expression(expr)?;
20167        }
20168        Ok(())
20169    }
20170
20171    fn generate_alter_set(&mut self, e: &AlterSet) -> Result<()> {
20172        // Python (Snowflake): return f"SET{exprs}{file_format}{copy_options}{tag}"
20173        self.write_keyword("SET");
20174
20175        // Generate option (e.g., AUTHORIZATION, LOGGED, UNLOGGED, etc.)
20176        if let Some(opt) = &e.option {
20177            self.write_space();
20178            self.generate_expression(opt)?;
20179        }
20180
20181        // Generate PROPERTIES (for Trino SET PROPERTIES x = y, ...)
20182        // Check if expressions look like property assignments
20183        if !e.expressions.is_empty() {
20184            // Check if this looks like property assignments (for SET PROPERTIES)
20185            let is_properties = e.expressions.iter().any(|expr| matches!(expr, Expression::Eq(_)));
20186            if is_properties && e.option.is_none() {
20187                self.write_space();
20188                self.write_keyword("PROPERTIES");
20189            }
20190            self.write_space();
20191            for (i, expr) in e.expressions.iter().enumerate() {
20192                if i > 0 {
20193                    self.write(", ");
20194                }
20195                self.generate_expression(expr)?;
20196            }
20197        }
20198
20199        // Generate STAGE_FILE_FORMAT = (...) with space-separated properties
20200        if let Some(file_format) = &e.file_format {
20201            self.write(" ");
20202            self.write_keyword("STAGE_FILE_FORMAT");
20203            self.write(" = (");
20204            self.generate_space_separated_properties(file_format)?;
20205            self.write(")");
20206        }
20207
20208        // Generate STAGE_COPY_OPTIONS = (...) with space-separated properties
20209        if let Some(copy_options) = &e.copy_options {
20210            self.write(" ");
20211            self.write_keyword("STAGE_COPY_OPTIONS");
20212            self.write(" = (");
20213            self.generate_space_separated_properties(copy_options)?;
20214            self.write(")");
20215        }
20216
20217        // Generate TAG ...
20218        if let Some(tag) = &e.tag {
20219            self.write(" ");
20220            self.write_keyword("TAG");
20221            self.write(" ");
20222            self.generate_expression(tag)?;
20223        }
20224
20225        Ok(())
20226    }
20227
20228    /// Generate space-separated properties (for Snowflake STAGE_FILE_FORMAT, etc.)
20229    fn generate_space_separated_properties(&mut self, expr: &Expression) -> Result<()> {
20230        match expr {
20231            Expression::Tuple(t) => {
20232                for (i, prop) in t.expressions.iter().enumerate() {
20233                    if i > 0 {
20234                        self.write(" ");
20235                    }
20236                    self.generate_expression(prop)?;
20237                }
20238            }
20239            _ => {
20240                self.generate_expression(expr)?;
20241            }
20242        }
20243        Ok(())
20244    }
20245
20246    fn generate_alter_sort_key(&mut self, e: &AlterSortKey) -> Result<()> {
20247        // Python: return f"ALTER{compound} SORTKEY {this or expressions}"
20248        self.write_keyword("ALTER");
20249        if e.compound.is_some() {
20250            self.write_space();
20251            self.write_keyword("COMPOUND");
20252        }
20253        self.write_space();
20254        self.write_keyword("SORTKEY");
20255        self.write_space();
20256        if let Some(this) = &e.this {
20257            self.generate_expression(this)?;
20258        } else if !e.expressions.is_empty() {
20259            self.write("(");
20260            for (i, expr) in e.expressions.iter().enumerate() {
20261                if i > 0 {
20262                    self.write(", ");
20263                }
20264                self.generate_expression(expr)?;
20265            }
20266            self.write(")");
20267        }
20268        Ok(())
20269    }
20270
20271    fn generate_analyze(&mut self, e: &Analyze) -> Result<()> {
20272        // Python: return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
20273        self.write_keyword("ANALYZE");
20274        if !e.options.is_empty() {
20275            self.write_space();
20276            for (i, opt) in e.options.iter().enumerate() {
20277                if i > 0 {
20278                    self.write_space();
20279                }
20280                // Write options as keywords (not identifiers) to avoid quoting reserved words like FULL
20281                if let Expression::Identifier(id) = opt {
20282                    self.write_keyword(&id.name);
20283                } else {
20284                    self.generate_expression(opt)?;
20285                }
20286            }
20287        }
20288        if let Some(kind) = &e.kind {
20289            self.write_space();
20290            self.write_keyword(kind);
20291        }
20292        if let Some(this) = &e.this {
20293            self.write_space();
20294            self.generate_expression(this)?;
20295        }
20296        // Column list: ANALYZE tbl(col1, col2) (PostgreSQL)
20297        if !e.columns.is_empty() {
20298            self.write("(");
20299            for (i, col) in e.columns.iter().enumerate() {
20300                if i > 0 {
20301                    self.write(", ");
20302                }
20303                self.write(col);
20304            }
20305            self.write(")");
20306        }
20307        if let Some(partition) = &e.partition {
20308            self.write_space();
20309            self.generate_expression(partition)?;
20310        }
20311        if let Some(mode) = &e.mode {
20312            self.write_space();
20313            self.generate_expression(mode)?;
20314        }
20315        if let Some(expression) = &e.expression {
20316            self.write_space();
20317            self.generate_expression(expression)?;
20318        }
20319        if !e.properties.is_empty() {
20320            self.write_space();
20321            self.write_keyword(self.config.with_properties_prefix);
20322            self.write(" (");
20323            for (i, prop) in e.properties.iter().enumerate() {
20324                if i > 0 {
20325                    self.write(", ");
20326                }
20327                self.generate_expression(prop)?;
20328            }
20329            self.write(")");
20330        }
20331        Ok(())
20332    }
20333
20334    fn generate_analyze_delete(&mut self, e: &AnalyzeDelete) -> Result<()> {
20335        // Python: return f"DELETE{kind} STATISTICS"
20336        self.write_keyword("DELETE");
20337        if let Some(kind) = &e.kind {
20338            self.write_space();
20339            self.write_keyword(kind);
20340        }
20341        self.write_space();
20342        self.write_keyword("STATISTICS");
20343        Ok(())
20344    }
20345
20346    fn generate_analyze_histogram(&mut self, e: &AnalyzeHistogram) -> Result<()> {
20347        // Python: return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
20348        // Write `this` (UPDATE or DROP) as keyword to avoid quoting reserved words
20349        if let Expression::Identifier(id) = e.this.as_ref() {
20350            self.write_keyword(&id.name);
20351        } else {
20352            self.generate_expression(&e.this)?;
20353        }
20354        self.write_space();
20355        self.write_keyword("HISTOGRAM ON");
20356        self.write_space();
20357        for (i, expr) in e.expressions.iter().enumerate() {
20358            if i > 0 {
20359                self.write(", ");
20360            }
20361            self.generate_expression(expr)?;
20362        }
20363        if let Some(expression) = &e.expression {
20364            self.write_space();
20365            self.generate_expression(expression)?;
20366        }
20367        if let Some(update_options) = &e.update_options {
20368            self.write_space();
20369            self.generate_expression(update_options)?;
20370            self.write_space();
20371            self.write_keyword("UPDATE");
20372        }
20373        Ok(())
20374    }
20375
20376    fn generate_analyze_list_chained_rows(&mut self, e: &AnalyzeListChainedRows) -> Result<()> {
20377        // Python: return f"LIST CHAINED ROWS{inner_expression}"
20378        self.write_keyword("LIST CHAINED ROWS");
20379        if let Some(expression) = &e.expression {
20380            self.write_space();
20381            self.write_keyword("INTO");
20382            self.write_space();
20383            self.generate_expression(expression)?;
20384        }
20385        Ok(())
20386    }
20387
20388    fn generate_analyze_sample(&mut self, e: &AnalyzeSample) -> Result<()> {
20389        // Python: return f"SAMPLE {sample} {kind}"
20390        self.write_keyword("SAMPLE");
20391        self.write_space();
20392        if let Some(sample) = &e.sample {
20393            self.generate_expression(sample)?;
20394            self.write_space();
20395        }
20396        self.write_keyword(&e.kind);
20397        Ok(())
20398    }
20399
20400    fn generate_analyze_statistics(&mut self, e: &AnalyzeStatistics) -> Result<()> {
20401        // Python: return f"{kind}{option} STATISTICS{this}{columns}"
20402        self.write_keyword(&e.kind);
20403        if let Some(option) = &e.option {
20404            self.write_space();
20405            self.generate_expression(option)?;
20406        }
20407        self.write_space();
20408        self.write_keyword("STATISTICS");
20409        if let Some(this) = &e.this {
20410            self.write_space();
20411            self.generate_expression(this)?;
20412        }
20413        if !e.expressions.is_empty() {
20414            self.write_space();
20415            for (i, expr) in e.expressions.iter().enumerate() {
20416                if i > 0 {
20417                    self.write(", ");
20418                }
20419                self.generate_expression(expr)?;
20420            }
20421        }
20422        Ok(())
20423    }
20424
20425    fn generate_analyze_validate(&mut self, e: &AnalyzeValidate) -> Result<()> {
20426        // Python: return f"VALIDATE {kind}{this}{inner_expression}"
20427        self.write_keyword("VALIDATE");
20428        self.write_space();
20429        self.write_keyword(&e.kind);
20430        if let Some(this) = &e.this {
20431            self.write_space();
20432            // this is a keyword string like "UPDATE", "CASCADE FAST", etc. - write as keywords
20433            if let Expression::Identifier(id) = this.as_ref() {
20434                self.write_keyword(&id.name);
20435            } else {
20436                self.generate_expression(this)?;
20437            }
20438        }
20439        if let Some(expression) = &e.expression {
20440            self.write_space();
20441            self.write_keyword("INTO");
20442            self.write_space();
20443            self.generate_expression(expression)?;
20444        }
20445        Ok(())
20446    }
20447
20448    fn generate_analyze_with(&mut self, e: &AnalyzeWith) -> Result<()> {
20449        // Python: return f"WITH {expressions}"
20450        self.write_keyword("WITH");
20451        self.write_space();
20452        for (i, expr) in e.expressions.iter().enumerate() {
20453            if i > 0 {
20454                self.write(", ");
20455            }
20456            self.generate_expression(expr)?;
20457        }
20458        Ok(())
20459    }
20460
20461    fn generate_anonymous(&mut self, e: &Anonymous) -> Result<()> {
20462        // Anonymous represents a generic function call: FUNC_NAME(args...)
20463        // Python: return self.func(self.sql(expression, "this"), *expression.expressions)
20464        self.generate_expression(&e.this)?;
20465        self.write("(");
20466        for (i, arg) in e.expressions.iter().enumerate() {
20467            if i > 0 {
20468                self.write(", ");
20469            }
20470            self.generate_expression(arg)?;
20471        }
20472        self.write(")");
20473        Ok(())
20474    }
20475
20476    fn generate_anonymous_agg_func(&mut self, e: &AnonymousAggFunc) -> Result<()> {
20477        // Same as Anonymous but for aggregate functions
20478        self.generate_expression(&e.this)?;
20479        self.write("(");
20480        for (i, arg) in e.expressions.iter().enumerate() {
20481            if i > 0 {
20482                self.write(", ");
20483            }
20484            self.generate_expression(arg)?;
20485        }
20486        self.write(")");
20487        Ok(())
20488    }
20489
20490    fn generate_apply(&mut self, e: &Apply) -> Result<()> {
20491        // Python: return f"{this} APPLY({expr})"
20492        self.generate_expression(&e.this)?;
20493        self.write_space();
20494        self.write_keyword("APPLY");
20495        self.write("(");
20496        self.generate_expression(&e.expression)?;
20497        self.write(")");
20498        Ok(())
20499    }
20500
20501    fn generate_approx_percentile_estimate(&mut self, e: &ApproxPercentileEstimate) -> Result<()> {
20502        // APPROX_PERCENTILE_ESTIMATE(this, percentile)
20503        self.write_keyword("APPROX_PERCENTILE_ESTIMATE");
20504        self.write("(");
20505        self.generate_expression(&e.this)?;
20506        if let Some(percentile) = &e.percentile {
20507            self.write(", ");
20508            self.generate_expression(percentile)?;
20509        }
20510        self.write(")");
20511        Ok(())
20512    }
20513
20514    fn generate_approx_quantile(&mut self, e: &ApproxQuantile) -> Result<()> {
20515        // APPROX_QUANTILE(this, quantile[, accuracy][, weight])
20516        self.write_keyword("APPROX_QUANTILE");
20517        self.write("(");
20518        self.generate_expression(&e.this)?;
20519        if let Some(quantile) = &e.quantile {
20520            self.write(", ");
20521            self.generate_expression(quantile)?;
20522        }
20523        if let Some(accuracy) = &e.accuracy {
20524            self.write(", ");
20525            self.generate_expression(accuracy)?;
20526        }
20527        if let Some(weight) = &e.weight {
20528            self.write(", ");
20529            self.generate_expression(weight)?;
20530        }
20531        self.write(")");
20532        Ok(())
20533    }
20534
20535    fn generate_approx_quantiles(&mut self, e: &ApproxQuantiles) -> Result<()> {
20536        // APPROX_QUANTILES(this, expression)
20537        self.write_keyword("APPROX_QUANTILES");
20538        self.write("(");
20539        self.generate_expression(&e.this)?;
20540        if let Some(expression) = &e.expression {
20541            self.write(", ");
20542            self.generate_expression(expression)?;
20543        }
20544        self.write(")");
20545        Ok(())
20546    }
20547
20548    fn generate_approx_top_k(&mut self, e: &ApproxTopK) -> Result<()> {
20549        // APPROX_TOP_K(this[, expression][, counters])
20550        self.write_keyword("APPROX_TOP_K");
20551        self.write("(");
20552        self.generate_expression(&e.this)?;
20553        if let Some(expression) = &e.expression {
20554            self.write(", ");
20555            self.generate_expression(expression)?;
20556        }
20557        if let Some(counters) = &e.counters {
20558            self.write(", ");
20559            self.generate_expression(counters)?;
20560        }
20561        self.write(")");
20562        Ok(())
20563    }
20564
20565    fn generate_approx_top_k_accumulate(&mut self, e: &ApproxTopKAccumulate) -> Result<()> {
20566        // APPROX_TOP_K_ACCUMULATE(this[, expression])
20567        self.write_keyword("APPROX_TOP_K_ACCUMULATE");
20568        self.write("(");
20569        self.generate_expression(&e.this)?;
20570        if let Some(expression) = &e.expression {
20571            self.write(", ");
20572            self.generate_expression(expression)?;
20573        }
20574        self.write(")");
20575        Ok(())
20576    }
20577
20578    fn generate_approx_top_k_combine(&mut self, e: &ApproxTopKCombine) -> Result<()> {
20579        // APPROX_TOP_K_COMBINE(this[, expression])
20580        self.write_keyword("APPROX_TOP_K_COMBINE");
20581        self.write("(");
20582        self.generate_expression(&e.this)?;
20583        if let Some(expression) = &e.expression {
20584            self.write(", ");
20585            self.generate_expression(expression)?;
20586        }
20587        self.write(")");
20588        Ok(())
20589    }
20590
20591    fn generate_approx_top_k_estimate(&mut self, e: &ApproxTopKEstimate) -> Result<()> {
20592        // APPROX_TOP_K_ESTIMATE(this[, expression])
20593        self.write_keyword("APPROX_TOP_K_ESTIMATE");
20594        self.write("(");
20595        self.generate_expression(&e.this)?;
20596        if let Some(expression) = &e.expression {
20597            self.write(", ");
20598            self.generate_expression(expression)?;
20599        }
20600        self.write(")");
20601        Ok(())
20602    }
20603
20604    fn generate_approx_top_sum(&mut self, e: &ApproxTopSum) -> Result<()> {
20605        // APPROX_TOP_SUM(this, expression[, count])
20606        self.write_keyword("APPROX_TOP_SUM");
20607        self.write("(");
20608        self.generate_expression(&e.this)?;
20609        self.write(", ");
20610        self.generate_expression(&e.expression)?;
20611        if let Some(count) = &e.count {
20612            self.write(", ");
20613            self.generate_expression(count)?;
20614        }
20615        self.write(")");
20616        Ok(())
20617    }
20618
20619    fn generate_arg_max(&mut self, e: &ArgMax) -> Result<()> {
20620        // ARG_MAX(this, expression[, count])
20621        self.write_keyword("ARG_MAX");
20622        self.write("(");
20623        self.generate_expression(&e.this)?;
20624        self.write(", ");
20625        self.generate_expression(&e.expression)?;
20626        if let Some(count) = &e.count {
20627            self.write(", ");
20628            self.generate_expression(count)?;
20629        }
20630        self.write(")");
20631        Ok(())
20632    }
20633
20634    fn generate_arg_min(&mut self, e: &ArgMin) -> Result<()> {
20635        // ARG_MIN(this, expression[, count])
20636        self.write_keyword("ARG_MIN");
20637        self.write("(");
20638        self.generate_expression(&e.this)?;
20639        self.write(", ");
20640        self.generate_expression(&e.expression)?;
20641        if let Some(count) = &e.count {
20642            self.write(", ");
20643            self.generate_expression(count)?;
20644        }
20645        self.write(")");
20646        Ok(())
20647    }
20648
20649    fn generate_array_all(&mut self, e: &ArrayAll) -> Result<()> {
20650        // ARRAY_ALL(this, expression)
20651        self.write_keyword("ARRAY_ALL");
20652        self.write("(");
20653        self.generate_expression(&e.this)?;
20654        self.write(", ");
20655        self.generate_expression(&e.expression)?;
20656        self.write(")");
20657        Ok(())
20658    }
20659
20660    fn generate_array_any(&mut self, e: &ArrayAny) -> Result<()> {
20661        // ARRAY_ANY(this, expression) - fallback implementation
20662        self.write_keyword("ARRAY_ANY");
20663        self.write("(");
20664        self.generate_expression(&e.this)?;
20665        self.write(", ");
20666        self.generate_expression(&e.expression)?;
20667        self.write(")");
20668        Ok(())
20669    }
20670
20671    fn generate_array_construct_compact(&mut self, e: &ArrayConstructCompact) -> Result<()> {
20672        // ARRAY_CONSTRUCT_COMPACT(expressions...)
20673        self.write_keyword("ARRAY_CONSTRUCT_COMPACT");
20674        self.write("(");
20675        for (i, expr) in e.expressions.iter().enumerate() {
20676            if i > 0 {
20677                self.write(", ");
20678            }
20679            self.generate_expression(expr)?;
20680        }
20681        self.write(")");
20682        Ok(())
20683    }
20684
20685    fn generate_array_sum(&mut self, e: &ArraySum) -> Result<()> {
20686        // ARRAY_SUM(this[, expression])
20687        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
20688            self.write("arraySum");
20689        } else {
20690            self.write_keyword("ARRAY_SUM");
20691        }
20692        self.write("(");
20693        self.generate_expression(&e.this)?;
20694        if let Some(expression) = &e.expression {
20695            self.write(", ");
20696            self.generate_expression(expression)?;
20697        }
20698        self.write(")");
20699        Ok(())
20700    }
20701
20702    fn generate_at_index(&mut self, e: &AtIndex) -> Result<()> {
20703        // Python: return f"{this} AT {index}"
20704        self.generate_expression(&e.this)?;
20705        self.write_space();
20706        self.write_keyword("AT");
20707        self.write_space();
20708        self.generate_expression(&e.expression)?;
20709        Ok(())
20710    }
20711
20712    fn generate_attach(&mut self, e: &Attach) -> Result<()> {
20713        // Python: return f"ATTACH{exists_sql} {this}{expressions}"
20714        self.write_keyword("ATTACH");
20715        if e.exists {
20716            self.write_space();
20717            self.write_keyword("IF NOT EXISTS");
20718        }
20719        self.write_space();
20720        self.generate_expression(&e.this)?;
20721        if !e.expressions.is_empty() {
20722            self.write(" (");
20723            for (i, expr) in e.expressions.iter().enumerate() {
20724                if i > 0 {
20725                    self.write(", ");
20726                }
20727                self.generate_expression(expr)?;
20728            }
20729            self.write(")");
20730        }
20731        Ok(())
20732    }
20733
20734    fn generate_attach_option(&mut self, e: &AttachOption) -> Result<()> {
20735        // AttachOption: this [expression]
20736        // Python sqlglot: no equals sign, just space-separated
20737        self.generate_expression(&e.this)?;
20738        if let Some(expression) = &e.expression {
20739            self.write_space();
20740            self.generate_expression(expression)?;
20741        }
20742        Ok(())
20743    }
20744
20745    /// Generate the auto_increment keyword and options for a column definition.
20746    /// Different dialects use different syntax: IDENTITY, AUTOINCREMENT, AUTO_INCREMENT,
20747    /// GENERATED AS IDENTITY, etc.
20748    fn generate_auto_increment_keyword(&mut self, col: &crate::expressions::ColumnDef) -> Result<()> {
20749        use crate::dialects::DialectType;
20750        if matches!(self.config.dialect, Some(DialectType::Redshift)) {
20751            self.write_keyword("IDENTITY");
20752            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20753                self.write("(");
20754                if let Some(ref start) = col.auto_increment_start {
20755                    self.generate_expression(start)?;
20756                } else {
20757                    self.write("0");
20758                }
20759                self.write(", ");
20760                if let Some(ref inc) = col.auto_increment_increment {
20761                    self.generate_expression(inc)?;
20762                } else {
20763                    self.write("1");
20764                }
20765                self.write(")");
20766            }
20767        } else if matches!(self.config.dialect, Some(DialectType::Snowflake) | Some(DialectType::SQLite)) {
20768            self.write_keyword("AUTOINCREMENT");
20769            if let Some(ref start) = col.auto_increment_start {
20770                self.write_space();
20771                self.write_keyword("START");
20772                self.write_space();
20773                self.generate_expression(start)?;
20774            }
20775            if let Some(ref inc) = col.auto_increment_increment {
20776                self.write_space();
20777                self.write_keyword("INCREMENT");
20778                self.write_space();
20779                self.generate_expression(inc)?;
20780            }
20781            if let Some(order) = col.auto_increment_order {
20782                self.write_space();
20783                if order {
20784                    self.write_keyword("ORDER");
20785                } else {
20786                    self.write_keyword("NOORDER");
20787                }
20788            }
20789        } else if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) {
20790            self.write_keyword("GENERATED BY DEFAULT AS IDENTITY");
20791            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20792                self.write(" (");
20793                let mut first = true;
20794                if let Some(ref start) = col.auto_increment_start {
20795                    self.write_keyword("START WITH");
20796                    self.write_space();
20797                    self.generate_expression(start)?;
20798                    first = false;
20799                }
20800                if let Some(ref inc) = col.auto_increment_increment {
20801                    if !first { self.write_space(); }
20802                    self.write_keyword("INCREMENT BY");
20803                    self.write_space();
20804                    self.generate_expression(inc)?;
20805                }
20806                self.write(")");
20807            }
20808        } else if matches!(self.config.dialect, Some(DialectType::Databricks)) {
20809            self.write_keyword("GENERATED ALWAYS AS IDENTITY");
20810            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20811                self.write(" (");
20812                let mut first = true;
20813                if let Some(ref start) = col.auto_increment_start {
20814                    self.write_keyword("START WITH");
20815                    self.write_space();
20816                    self.generate_expression(start)?;
20817                    first = false;
20818                }
20819                if let Some(ref inc) = col.auto_increment_increment {
20820                    if !first { self.write_space(); }
20821                    self.write_keyword("INCREMENT BY");
20822                    self.write_space();
20823                    self.generate_expression(inc)?;
20824                }
20825                self.write(")");
20826            }
20827        } else if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
20828            self.write_keyword("IDENTITY");
20829            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20830                self.write("(");
20831                if let Some(ref start) = col.auto_increment_start {
20832                    self.generate_expression(start)?;
20833                } else {
20834                    self.write("0");
20835                }
20836                self.write(", ");
20837                if let Some(ref inc) = col.auto_increment_increment {
20838                    self.generate_expression(inc)?;
20839                } else {
20840                    self.write("1");
20841                }
20842                self.write(")");
20843            }
20844        } else {
20845            self.write_keyword("AUTO_INCREMENT");
20846            if let Some(ref start) = col.auto_increment_start {
20847                self.write_space();
20848                self.write_keyword("START");
20849                self.write_space();
20850                self.generate_expression(start)?;
20851            }
20852            if let Some(ref inc) = col.auto_increment_increment {
20853                self.write_space();
20854                self.write_keyword("INCREMENT");
20855                self.write_space();
20856                self.generate_expression(inc)?;
20857            }
20858            if let Some(order) = col.auto_increment_order {
20859                self.write_space();
20860                if order {
20861                    self.write_keyword("ORDER");
20862                } else {
20863                    self.write_keyword("NOORDER");
20864                }
20865            }
20866        }
20867        Ok(())
20868    }
20869
20870    fn generate_auto_increment_property(&mut self, e: &AutoIncrementProperty) -> Result<()> {
20871        // AUTO_INCREMENT=value
20872        self.write_keyword("AUTO_INCREMENT");
20873        self.write("=");
20874        self.generate_expression(&e.this)?;
20875        Ok(())
20876    }
20877
20878    fn generate_auto_refresh_property(&mut self, e: &AutoRefreshProperty) -> Result<()> {
20879        // AUTO_REFRESH=value
20880        self.write_keyword("AUTO_REFRESH");
20881        self.write("=");
20882        self.generate_expression(&e.this)?;
20883        Ok(())
20884    }
20885
20886    fn generate_backup_property(&mut self, e: &BackupProperty) -> Result<()> {
20887        // BACKUP YES|NO (Redshift syntax uses space, not equals)
20888        self.write_keyword("BACKUP");
20889        self.write_space();
20890        self.generate_expression(&e.this)?;
20891        Ok(())
20892    }
20893
20894    fn generate_base64_decode_binary(&mut self, e: &Base64DecodeBinary) -> Result<()> {
20895        // BASE64_DECODE_BINARY(this[, alphabet])
20896        self.write_keyword("BASE64_DECODE_BINARY");
20897        self.write("(");
20898        self.generate_expression(&e.this)?;
20899        if let Some(alphabet) = &e.alphabet {
20900            self.write(", ");
20901            self.generate_expression(alphabet)?;
20902        }
20903        self.write(")");
20904        Ok(())
20905    }
20906
20907    fn generate_base64_decode_string(&mut self, e: &Base64DecodeString) -> Result<()> {
20908        // BASE64_DECODE_STRING(this[, alphabet])
20909        self.write_keyword("BASE64_DECODE_STRING");
20910        self.write("(");
20911        self.generate_expression(&e.this)?;
20912        if let Some(alphabet) = &e.alphabet {
20913            self.write(", ");
20914            self.generate_expression(alphabet)?;
20915        }
20916        self.write(")");
20917        Ok(())
20918    }
20919
20920    fn generate_base64_encode(&mut self, e: &Base64Encode) -> Result<()> {
20921        // BASE64_ENCODE(this[, max_line_length][, alphabet])
20922        self.write_keyword("BASE64_ENCODE");
20923        self.write("(");
20924        self.generate_expression(&e.this)?;
20925        if let Some(max_line_length) = &e.max_line_length {
20926            self.write(", ");
20927            self.generate_expression(max_line_length)?;
20928        }
20929        if let Some(alphabet) = &e.alphabet {
20930            self.write(", ");
20931            self.generate_expression(alphabet)?;
20932        }
20933        self.write(")");
20934        Ok(())
20935    }
20936
20937    fn generate_block_compression_property(&mut self, e: &BlockCompressionProperty) -> Result<()> {
20938        // BLOCKCOMPRESSION=... (complex Teradata property)
20939        self.write_keyword("BLOCKCOMPRESSION");
20940        self.write("=");
20941        if let Some(autotemp) = &e.autotemp {
20942            self.write_keyword("AUTOTEMP");
20943            self.write("(");
20944            self.generate_expression(autotemp)?;
20945            self.write(")");
20946        }
20947        if let Some(always) = &e.always {
20948            self.generate_expression(always)?;
20949        }
20950        if let Some(default) = &e.default {
20951            self.generate_expression(default)?;
20952        }
20953        if let Some(manual) = &e.manual {
20954            self.generate_expression(manual)?;
20955        }
20956        if let Some(never) = &e.never {
20957            self.generate_expression(never)?;
20958        }
20959        Ok(())
20960    }
20961
20962    fn generate_booland(&mut self, e: &Booland) -> Result<()> {
20963        // Python: return f"(({self.sql(expression, 'this')}) AND ({self.sql(expression, 'expression')}))"
20964        self.write("((");
20965        self.generate_expression(&e.this)?;
20966        self.write(") ");
20967        self.write_keyword("AND");
20968        self.write(" (");
20969        self.generate_expression(&e.expression)?;
20970        self.write("))");
20971        Ok(())
20972    }
20973
20974    fn generate_boolor(&mut self, e: &Boolor) -> Result<()> {
20975        // Python: return f"(({self.sql(expression, 'this')}) OR ({self.sql(expression, 'expression')}))"
20976        self.write("((");
20977        self.generate_expression(&e.this)?;
20978        self.write(") ");
20979        self.write_keyword("OR");
20980        self.write(" (");
20981        self.generate_expression(&e.expression)?;
20982        self.write("))");
20983        Ok(())
20984    }
20985
20986    fn generate_build_property(&mut self, e: &BuildProperty) -> Result<()> {
20987        // BUILD value (e.g., BUILD IMMEDIATE, BUILD DEFERRED)
20988        self.write_keyword("BUILD");
20989        self.write_space();
20990        self.generate_expression(&e.this)?;
20991        Ok(())
20992    }
20993
20994    fn generate_byte_string(&mut self, e: &ByteString) -> Result<()> {
20995        // Byte string literal like B'...' or X'...'
20996        self.generate_expression(&e.this)?;
20997        Ok(())
20998    }
20999
21000    fn generate_case_specific_column_constraint(&mut self, e: &CaseSpecificColumnConstraint) -> Result<()> {
21001        // CASESPECIFIC or NOT CASESPECIFIC (Teradata)
21002        if e.not_.is_some() {
21003            self.write_keyword("NOT");
21004            self.write_space();
21005        }
21006        self.write_keyword("CASESPECIFIC");
21007        Ok(())
21008    }
21009
21010    fn generate_cast_to_str_type(&mut self, e: &CastToStrType) -> Result<()> {
21011        // Cast to string type (dialect-specific)
21012        self.write_keyword("CAST");
21013        self.write("(");
21014        self.generate_expression(&e.this)?;
21015        if self.config.dialect == Some(DialectType::ClickHouse) {
21016            // ClickHouse: CAST(expr, 'type_string')
21017            self.write(", ");
21018        } else {
21019            self.write_space();
21020            self.write_keyword("AS");
21021            self.write_space();
21022        }
21023        if let Some(to) = &e.to {
21024            self.generate_expression(to)?;
21025        }
21026        self.write(")");
21027        Ok(())
21028    }
21029
21030    fn generate_changes(&mut self, e: &Changes) -> Result<()> {
21031        // CHANGES (INFORMATION => value) AT|BEFORE (...) END (...)
21032        // Python: f"CHANGES ({information}){at_before}{end}"
21033        self.write_keyword("CHANGES");
21034        self.write(" (");
21035        if let Some(information) = &e.information {
21036            self.write_keyword("INFORMATION");
21037            self.write(" => ");
21038            self.generate_expression(information)?;
21039        }
21040        self.write(")");
21041        // at_before and end are HistoricalData expressions that generate their own keywords
21042        if let Some(at_before) = &e.at_before {
21043            self.write(" ");
21044            self.generate_expression(at_before)?;
21045        }
21046        if let Some(end) = &e.end {
21047            self.write(" ");
21048            self.generate_expression(end)?;
21049        }
21050        Ok(())
21051    }
21052
21053    fn generate_character_set_column_constraint(&mut self, e: &CharacterSetColumnConstraint) -> Result<()> {
21054        // CHARACTER SET charset_name
21055        self.write_keyword("CHARACTER SET");
21056        self.write_space();
21057        self.generate_expression(&e.this)?;
21058        Ok(())
21059    }
21060
21061    fn generate_character_set_property(&mut self, e: &CharacterSetProperty) -> Result<()> {
21062        // [DEFAULT] CHARACTER SET=value
21063        if e.default.is_some() {
21064            self.write_keyword("DEFAULT");
21065            self.write_space();
21066        }
21067        self.write_keyword("CHARACTER SET");
21068        self.write("=");
21069        self.generate_expression(&e.this)?;
21070        Ok(())
21071    }
21072
21073    fn generate_check_column_constraint(&mut self, e: &CheckColumnConstraint) -> Result<()> {
21074        // Python: return f"CHECK ({self.sql(expression, 'this')}){enforced}"
21075        self.write_keyword("CHECK");
21076        self.write(" (");
21077        self.generate_expression(&e.this)?;
21078        self.write(")");
21079        if e.enforced.is_some() {
21080            self.write_space();
21081            self.write_keyword("ENFORCED");
21082        }
21083        Ok(())
21084    }
21085
21086    fn generate_check_json(&mut self, e: &CheckJson) -> Result<()> {
21087        // CHECK_JSON(this)
21088        self.write_keyword("CHECK_JSON");
21089        self.write("(");
21090        self.generate_expression(&e.this)?;
21091        self.write(")");
21092        Ok(())
21093    }
21094
21095    fn generate_check_xml(&mut self, e: &CheckXml) -> Result<()> {
21096        // CHECK_XML(this)
21097        self.write_keyword("CHECK_XML");
21098        self.write("(");
21099        self.generate_expression(&e.this)?;
21100        self.write(")");
21101        Ok(())
21102    }
21103
21104    fn generate_checksum_property(&mut self, e: &ChecksumProperty) -> Result<()> {
21105        // CHECKSUM=[ON|OFF|DEFAULT]
21106        self.write_keyword("CHECKSUM");
21107        self.write("=");
21108        if e.on.is_some() {
21109            self.write_keyword("ON");
21110        } else if e.default.is_some() {
21111            self.write_keyword("DEFAULT");
21112        } else {
21113            self.write_keyword("OFF");
21114        }
21115        Ok(())
21116    }
21117
21118    fn generate_clone(&mut self, e: &Clone) -> Result<()> {
21119        // Python: return f"{shallow}{keyword} {this}"
21120        if e.shallow.is_some() {
21121            self.write_keyword("SHALLOW");
21122            self.write_space();
21123        }
21124        if e.copy.is_some() {
21125            self.write_keyword("COPY");
21126        } else {
21127            self.write_keyword("CLONE");
21128        }
21129        self.write_space();
21130        self.generate_expression(&e.this)?;
21131        Ok(())
21132    }
21133
21134    fn generate_cluster_by(&mut self, e: &ClusterBy) -> Result<()> {
21135        // CLUSTER BY (expressions)
21136        self.write_keyword("CLUSTER BY");
21137        self.write(" (");
21138        for (i, ord) in e.expressions.iter().enumerate() {
21139            if i > 0 {
21140                self.write(", ");
21141            }
21142            self.generate_ordered(ord)?;
21143        }
21144        self.write(")");
21145        Ok(())
21146    }
21147
21148    fn generate_clustered_by_property(&mut self, e: &ClusteredByProperty) -> Result<()> {
21149        // Python: return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
21150        self.write_keyword("CLUSTERED BY");
21151        self.write(" (");
21152        for (i, expr) in e.expressions.iter().enumerate() {
21153            if i > 0 {
21154                self.write(", ");
21155            }
21156            self.generate_expression(expr)?;
21157        }
21158        self.write(")");
21159        if let Some(sorted_by) = &e.sorted_by {
21160            self.write_space();
21161            self.write_keyword("SORTED BY");
21162            self.write(" (");
21163            // Unwrap Tuple to avoid double parentheses
21164            if let Expression::Tuple(t) = sorted_by.as_ref() {
21165                for (i, expr) in t.expressions.iter().enumerate() {
21166                    if i > 0 {
21167                        self.write(", ");
21168                    }
21169                    self.generate_expression(expr)?;
21170                }
21171            } else {
21172                self.generate_expression(sorted_by)?;
21173            }
21174            self.write(")");
21175        }
21176        if let Some(buckets) = &e.buckets {
21177            self.write_space();
21178            self.write_keyword("INTO");
21179            self.write_space();
21180            self.generate_expression(buckets)?;
21181            self.write_space();
21182            self.write_keyword("BUCKETS");
21183        }
21184        Ok(())
21185    }
21186
21187    fn generate_collate_property(&mut self, e: &CollateProperty) -> Result<()> {
21188        // [DEFAULT] COLLATE [=] value
21189        // BigQuery uses space: DEFAULT COLLATE 'en'
21190        // Others use equals: COLLATE='en'
21191        if e.default.is_some() {
21192            self.write_keyword("DEFAULT");
21193            self.write_space();
21194        }
21195        self.write_keyword("COLLATE");
21196        // BigQuery uses space between COLLATE and value
21197        match self.config.dialect {
21198            Some(DialectType::BigQuery) => self.write_space(),
21199            _ => self.write("="),
21200        }
21201        self.generate_expression(&e.this)?;
21202        Ok(())
21203    }
21204
21205    fn generate_column_constraint(&mut self, e: &ColumnConstraint) -> Result<()> {
21206        // ColumnConstraint is an enum
21207        match e {
21208            ColumnConstraint::NotNull => {
21209                self.write_keyword("NOT NULL");
21210            }
21211            ColumnConstraint::Null => {
21212                self.write_keyword("NULL");
21213            }
21214            ColumnConstraint::Unique => {
21215                self.write_keyword("UNIQUE");
21216            }
21217            ColumnConstraint::PrimaryKey => {
21218                self.write_keyword("PRIMARY KEY");
21219            }
21220            ColumnConstraint::Default(expr) => {
21221                self.write_keyword("DEFAULT");
21222                self.write_space();
21223                self.generate_expression(expr)?;
21224            }
21225            ColumnConstraint::Check(expr) => {
21226                self.write_keyword("CHECK");
21227                self.write(" (");
21228                self.generate_expression(expr)?;
21229                self.write(")");
21230            }
21231            ColumnConstraint::References(fk_ref) => {
21232                if fk_ref.has_foreign_key_keywords {
21233                    self.write_keyword("FOREIGN KEY");
21234                    self.write_space();
21235                }
21236                self.write_keyword("REFERENCES");
21237                self.write_space();
21238                self.generate_table(&fk_ref.table)?;
21239                if !fk_ref.columns.is_empty() {
21240                    self.write(" (");
21241                    for (i, col) in fk_ref.columns.iter().enumerate() {
21242                        if i > 0 {
21243                            self.write(", ");
21244                        }
21245                        self.generate_identifier(col)?;
21246                    }
21247                    self.write(")");
21248                }
21249            }
21250            ColumnConstraint::GeneratedAsIdentity(gen) => {
21251                self.write_keyword("GENERATED");
21252                self.write_space();
21253                if gen.always {
21254                    self.write_keyword("ALWAYS");
21255                } else {
21256                    self.write_keyword("BY DEFAULT");
21257                    if gen.on_null {
21258                        self.write_space();
21259                        self.write_keyword("ON NULL");
21260                    }
21261                }
21262                self.write_space();
21263                self.write_keyword("AS IDENTITY");
21264            }
21265            ColumnConstraint::Collate(collation) => {
21266                self.write_keyword("COLLATE");
21267                self.write_space();
21268                self.generate_identifier(collation)?;
21269            }
21270            ColumnConstraint::Comment(comment) => {
21271                self.write_keyword("COMMENT");
21272                self.write(" '");
21273                self.write(comment);
21274                self.write("'");
21275            }
21276            ColumnConstraint::ComputedColumn(cc) => {
21277                self.generate_computed_column_inline(cc)?;
21278            }
21279            ColumnConstraint::GeneratedAsRow(gar) => {
21280                self.generate_generated_as_row_inline(gar)?;
21281            }
21282            ColumnConstraint::Tags(tags) => {
21283                self.write_keyword("TAG");
21284                self.write(" (");
21285                for (i, expr) in tags.expressions.iter().enumerate() {
21286                    if i > 0 {
21287                        self.write(", ");
21288                    }
21289                    self.generate_expression(expr)?;
21290                }
21291                self.write(")");
21292            }
21293            ColumnConstraint::Path(path_expr) => {
21294                self.write_keyword("PATH");
21295                self.write_space();
21296                self.generate_expression(path_expr)?;
21297            }
21298        }
21299        Ok(())
21300    }
21301
21302    fn generate_column_position(&mut self, e: &ColumnPosition) -> Result<()> {
21303        // ColumnPosition is an enum
21304        match e {
21305            ColumnPosition::First => {
21306                self.write_keyword("FIRST");
21307            }
21308            ColumnPosition::After(ident) => {
21309                self.write_keyword("AFTER");
21310                self.write_space();
21311                self.generate_identifier(ident)?;
21312            }
21313        }
21314        Ok(())
21315    }
21316
21317    fn generate_column_prefix(&mut self, e: &ColumnPrefix) -> Result<()> {
21318        // column(prefix)
21319        self.generate_expression(&e.this)?;
21320        self.write("(");
21321        self.generate_expression(&e.expression)?;
21322        self.write(")");
21323        Ok(())
21324    }
21325
21326    fn generate_columns(&mut self, e: &Columns) -> Result<()> {
21327        // If unpack is true, this came from * COLUMNS(pattern)
21328        // DuckDB syntax: * COLUMNS(c ILIKE '%suffix') or COLUMNS(pattern)
21329        if let Some(ref unpack) = e.unpack {
21330            if let Expression::Boolean(b) = unpack.as_ref() {
21331                if b.value {
21332                    self.write("*");
21333                }
21334            }
21335        }
21336        self.write_keyword("COLUMNS");
21337        self.write("(");
21338        self.generate_expression(&e.this)?;
21339        self.write(")");
21340        Ok(())
21341    }
21342
21343    fn generate_combined_agg_func(&mut self, e: &CombinedAggFunc) -> Result<()> {
21344        // Combined aggregate: FUNC(args) combined
21345        self.generate_expression(&e.this)?;
21346        self.write("(");
21347        for (i, expr) in e.expressions.iter().enumerate() {
21348            if i > 0 {
21349                self.write(", ");
21350            }
21351            self.generate_expression(expr)?;
21352        }
21353        self.write(")");
21354        Ok(())
21355    }
21356
21357    fn generate_combined_parameterized_agg(&mut self, e: &CombinedParameterizedAgg) -> Result<()> {
21358        // Combined parameterized aggregate: FUNC(params)(expressions)
21359        self.generate_expression(&e.this)?;
21360        self.write("(");
21361        for (i, param) in e.params.iter().enumerate() {
21362            if i > 0 {
21363                self.write(", ");
21364            }
21365            self.generate_expression(param)?;
21366        }
21367        self.write(")(");
21368        for (i, expr) in e.expressions.iter().enumerate() {
21369            if i > 0 {
21370                self.write(", ");
21371            }
21372            self.generate_expression(expr)?;
21373        }
21374        self.write(")");
21375        Ok(())
21376    }
21377
21378    fn generate_commit(&mut self, e: &Commit) -> Result<()> {
21379        // COMMIT [TRANSACTION [transaction_name]] [WITH (DELAYED_DURABILITY = ON|OFF)] [AND [NO] CHAIN]
21380        self.write_keyword("COMMIT");
21381
21382        // TSQL always uses COMMIT TRANSACTION
21383        if e.this.is_none() && matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21384            self.write_space();
21385            self.write_keyword("TRANSACTION");
21386        }
21387
21388        // Check if this has TRANSACTION keyword or transaction name
21389        if let Some(this) = &e.this {
21390            // Check if it's just the "TRANSACTION" marker or an actual transaction name
21391            let is_transaction_marker = matches!(
21392                this.as_ref(),
21393                Expression::Identifier(id) if id.name == "TRANSACTION"
21394            );
21395
21396            self.write_space();
21397            self.write_keyword("TRANSACTION");
21398
21399            // If it's a real transaction name, output it
21400            if !is_transaction_marker {
21401                self.write_space();
21402                self.generate_expression(this)?;
21403            }
21404        }
21405
21406        // Output WITH (DELAYED_DURABILITY = ON|OFF) for TSQL
21407        if let Some(durability) = &e.durability {
21408            self.write_space();
21409            self.write_keyword("WITH");
21410            self.write(" (");
21411            self.write_keyword("DELAYED_DURABILITY");
21412            self.write(" = ");
21413            if let Expression::Boolean(BooleanLiteral { value: true }) = durability.as_ref() {
21414                self.write_keyword("ON");
21415            } else {
21416                self.write_keyword("OFF");
21417            }
21418            self.write(")");
21419        }
21420
21421        // Output AND [NO] CHAIN
21422        if let Some(chain) = &e.chain {
21423            self.write_space();
21424            if let Expression::Boolean(BooleanLiteral { value: false }) = chain.as_ref() {
21425                self.write_keyword("AND NO CHAIN");
21426            } else {
21427                self.write_keyword("AND CHAIN");
21428            }
21429        }
21430        Ok(())
21431    }
21432
21433    fn generate_comprehension(&mut self, e: &Comprehension) -> Result<()> {
21434        // Python-style comprehension: [expr FOR var[, pos] IN iterator IF condition]
21435        self.write("[");
21436        self.generate_expression(&e.this)?;
21437        self.write_space();
21438        self.write_keyword("FOR");
21439        self.write_space();
21440        self.generate_expression(&e.expression)?;
21441        // Handle optional position variable (for enumerate-like syntax)
21442        if let Some(pos) = &e.position {
21443            self.write(", ");
21444            self.generate_expression(pos)?;
21445        }
21446        if let Some(iterator) = &e.iterator {
21447            self.write_space();
21448            self.write_keyword("IN");
21449            self.write_space();
21450            self.generate_expression(iterator)?;
21451        }
21452        if let Some(condition) = &e.condition {
21453            self.write_space();
21454            self.write_keyword("IF");
21455            self.write_space();
21456            self.generate_expression(condition)?;
21457        }
21458        self.write("]");
21459        Ok(())
21460    }
21461
21462    fn generate_compress(&mut self, e: &Compress) -> Result<()> {
21463        // COMPRESS(this[, method])
21464        self.write_keyword("COMPRESS");
21465        self.write("(");
21466        self.generate_expression(&e.this)?;
21467        if let Some(method) = &e.method {
21468            self.write(", '");
21469            self.write(method);
21470            self.write("'");
21471        }
21472        self.write(")");
21473        Ok(())
21474    }
21475
21476    fn generate_compress_column_constraint(&mut self, e: &CompressColumnConstraint) -> Result<()> {
21477        // Python: return f"COMPRESS {this}"
21478        self.write_keyword("COMPRESS");
21479        if let Some(this) = &e.this {
21480            self.write_space();
21481            self.generate_expression(this)?;
21482        }
21483        Ok(())
21484    }
21485
21486    fn generate_computed_column_constraint(&mut self, e: &ComputedColumnConstraint) -> Result<()> {
21487        // Python: return f"AS {this}{persisted}"
21488        self.write_keyword("AS");
21489        self.write_space();
21490        self.generate_expression(&e.this)?;
21491        if e.not_null.is_some() {
21492            self.write_space();
21493            self.write_keyword("PERSISTED NOT NULL");
21494        } else if e.persisted.is_some() {
21495            self.write_space();
21496            self.write_keyword("PERSISTED");
21497        }
21498        Ok(())
21499    }
21500
21501    /// Generate a ComputedColumn constraint inline within a column definition.
21502    /// Handles MySQL/PostgreSQL: GENERATED ALWAYS AS (expr) STORED|VIRTUAL
21503    /// Handles TSQL: AS (expr) [PERSISTED] [NOT NULL]
21504    fn generate_computed_column_inline(&mut self, cc: &ComputedColumn) -> Result<()> {
21505        let computed_expr = if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21506            match &*cc.expression {
21507                Expression::Year(y)
21508                    if !matches!(&y.this, Expression::Cast(c) if matches!(c.to, DataType::Date)) =>
21509                {
21510                    let wrapped = Expression::Cast(Box::new(Cast {
21511                        this: y.this.clone(),
21512                        to: DataType::Date,
21513                        trailing_comments: Vec::new(),
21514                        double_colon_syntax: false,
21515                        format: None,
21516                        default: None,
21517                    }));
21518                    Expression::Year(Box::new(UnaryFunc::new(wrapped)))
21519                }
21520                Expression::Function(f)
21521                    if f.name.eq_ignore_ascii_case("YEAR")
21522                        && f.args.len() == 1
21523                        && !matches!(&f.args[0], Expression::Cast(c) if matches!(c.to, DataType::Date)) =>
21524                {
21525                    let wrapped = Expression::Cast(Box::new(Cast {
21526                        this: f.args[0].clone(),
21527                        to: DataType::Date,
21528                        trailing_comments: Vec::new(),
21529                        double_colon_syntax: false,
21530                        format: None,
21531                        default: None,
21532                    }));
21533                    Expression::Function(Box::new(Function::new("YEAR".to_string(), vec![wrapped])))
21534                }
21535                _ => *cc.expression.clone(),
21536            }
21537        } else {
21538            *cc.expression.clone()
21539        };
21540
21541        match cc.persistence_kind.as_deref() {
21542            Some("STORED") | Some("VIRTUAL") => {
21543                // MySQL/PostgreSQL: GENERATED ALWAYS AS (expr) STORED|VIRTUAL
21544                self.write_keyword("GENERATED ALWAYS AS");
21545                self.write(" (");
21546                self.generate_expression(&computed_expr)?;
21547                self.write(")");
21548                self.write_space();
21549                if cc.persisted {
21550                    self.write_keyword("STORED");
21551                } else {
21552                    self.write_keyword("VIRTUAL");
21553                }
21554            }
21555            Some("PERSISTED") => {
21556                // TSQL/SingleStore: AS (expr) PERSISTED [TYPE] [NOT NULL]
21557                self.write_keyword("AS");
21558                self.write(" (");
21559                self.generate_expression(&computed_expr)?;
21560                self.write(")");
21561                self.write_space();
21562                self.write_keyword("PERSISTED");
21563                // Output data type if present (SingleStore: PERSISTED TYPE NOT NULL)
21564                if let Some(ref dt) = cc.data_type {
21565                    self.write_space();
21566                    self.generate_data_type(dt)?;
21567                }
21568                if cc.not_null {
21569                    self.write_space();
21570                    self.write_keyword("NOT NULL");
21571                }
21572            }
21573            _ => {
21574                // Spark/Databricks/Hive: GENERATED ALWAYS AS (expr)
21575                // TSQL computed column without PERSISTED: AS (expr)
21576                if matches!(
21577                    self.config.dialect,
21578                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive)
21579                ) {
21580                    self.write_keyword("GENERATED ALWAYS AS");
21581                    self.write(" (");
21582                    self.generate_expression(&computed_expr)?;
21583                    self.write(")");
21584                } else if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21585                    self.write_keyword("AS");
21586                    let omit_parens = matches!(computed_expr, Expression::Year(_))
21587                        || matches!(&computed_expr, Expression::Function(f) if f.name.eq_ignore_ascii_case("YEAR"));
21588                    if omit_parens {
21589                        self.write_space();
21590                        self.generate_expression(&computed_expr)?;
21591                    } else {
21592                        self.write(" (");
21593                        self.generate_expression(&computed_expr)?;
21594                        self.write(")");
21595                    }
21596                } else {
21597                    self.write_keyword("AS");
21598                    self.write(" (");
21599                    self.generate_expression(&computed_expr)?;
21600                    self.write(")");
21601                }
21602            }
21603        }
21604        Ok(())
21605    }
21606
21607    /// Generate a GeneratedAsRow constraint inline within a column definition.
21608    /// TSQL temporal: GENERATED ALWAYS AS ROW START|END [HIDDEN]
21609    fn generate_generated_as_row_inline(&mut self, gar: &GeneratedAsRow) -> Result<()> {
21610        self.write_keyword("GENERATED ALWAYS AS ROW ");
21611        if gar.start {
21612            self.write_keyword("START");
21613        } else {
21614            self.write_keyword("END");
21615        }
21616        if gar.hidden {
21617            self.write_space();
21618            self.write_keyword("HIDDEN");
21619        }
21620        Ok(())
21621    }
21622
21623    /// Generate just the SYSTEM_VERSIONING=ON(...) content without WITH() wrapper.
21624    fn generate_system_versioning_content(&mut self, e: &WithSystemVersioningProperty) -> Result<()> {
21625        let mut parts = Vec::new();
21626
21627        if let Some(this) = &e.this {
21628            let mut s = String::from("HISTORY_TABLE=");
21629            let mut gen = Generator::new();
21630            gen.config = self.config.clone();
21631            gen.generate_expression(this)?;
21632            s.push_str(&gen.output);
21633            parts.push(s);
21634        }
21635
21636        if let Some(data_consistency) = &e.data_consistency {
21637            let mut s = String::from("DATA_CONSISTENCY_CHECK=");
21638            let mut gen = Generator::new();
21639            gen.config = self.config.clone();
21640            gen.generate_expression(data_consistency)?;
21641            s.push_str(&gen.output);
21642            parts.push(s);
21643        }
21644
21645        if let Some(retention_period) = &e.retention_period {
21646            let mut s = String::from("HISTORY_RETENTION_PERIOD=");
21647            let mut gen = Generator::new();
21648            gen.config = self.config.clone();
21649            gen.generate_expression(retention_period)?;
21650            s.push_str(&gen.output);
21651            parts.push(s);
21652        }
21653
21654        self.write_keyword("SYSTEM_VERSIONING");
21655        self.write("=");
21656
21657        if !parts.is_empty() {
21658            self.write_keyword("ON");
21659            self.write("(");
21660            self.write(&parts.join(", "));
21661            self.write(")");
21662        } else if e.on.is_some() {
21663            self.write_keyword("ON");
21664        } else {
21665            self.write_keyword("OFF");
21666        }
21667
21668        Ok(())
21669    }
21670
21671    fn generate_conditional_insert(&mut self, e: &ConditionalInsert) -> Result<()> {
21672        // Conditional INSERT for multi-table inserts
21673        // Output: [WHEN cond THEN | ELSE] INTO table [(cols)] [VALUES (...)]
21674        if e.else_.is_some() {
21675            self.write_keyword("ELSE");
21676            self.write_space();
21677        } else if let Some(expression) = &e.expression {
21678            self.write_keyword("WHEN");
21679            self.write_space();
21680            self.generate_expression(expression)?;
21681            self.write_space();
21682            self.write_keyword("THEN");
21683            self.write_space();
21684        }
21685
21686        // Handle Insert expression specially - output "INTO table (cols) VALUES (...)"
21687        // without the "INSERT " prefix
21688        if let Expression::Insert(insert) = e.this.as_ref() {
21689            self.write_keyword("INTO");
21690            self.write_space();
21691            self.generate_table(&insert.table)?;
21692
21693            // Optional column list
21694            if !insert.columns.is_empty() {
21695                self.write(" (");
21696                for (i, col) in insert.columns.iter().enumerate() {
21697                    if i > 0 {
21698                        self.write(", ");
21699                    }
21700                    self.generate_identifier(col)?;
21701                }
21702                self.write(")");
21703            }
21704
21705            // Optional VALUES clause
21706            if !insert.values.is_empty() {
21707                self.write_space();
21708                self.write_keyword("VALUES");
21709                for (row_idx, row) in insert.values.iter().enumerate() {
21710                    if row_idx > 0 {
21711                        self.write(", ");
21712                    }
21713                    self.write(" (");
21714                    for (i, val) in row.iter().enumerate() {
21715                        if i > 0 {
21716                            self.write(", ");
21717                        }
21718                        self.generate_expression(val)?;
21719                    }
21720                    self.write(")");
21721                }
21722            }
21723        } else {
21724            // Fallback for non-Insert expressions
21725            self.generate_expression(&e.this)?;
21726        }
21727        Ok(())
21728    }
21729
21730    fn generate_constraint(&mut self, e: &Constraint) -> Result<()> {
21731        // Python: return f"CONSTRAINT {this} {expressions}"
21732        self.write_keyword("CONSTRAINT");
21733        self.write_space();
21734        self.generate_expression(&e.this)?;
21735        if !e.expressions.is_empty() {
21736            self.write_space();
21737            for (i, expr) in e.expressions.iter().enumerate() {
21738                if i > 0 {
21739                    self.write_space();
21740                }
21741                self.generate_expression(expr)?;
21742            }
21743        }
21744        Ok(())
21745    }
21746
21747    fn generate_convert_timezone(&mut self, e: &ConvertTimezone) -> Result<()> {
21748        // CONVERT_TIMEZONE([source_tz,] target_tz, timestamp)
21749        self.write_keyword("CONVERT_TIMEZONE");
21750        self.write("(");
21751        let mut first = true;
21752        if let Some(source_tz) = &e.source_tz {
21753            self.generate_expression(source_tz)?;
21754            first = false;
21755        }
21756        if let Some(target_tz) = &e.target_tz {
21757            if !first {
21758                self.write(", ");
21759            }
21760            self.generate_expression(target_tz)?;
21761            first = false;
21762        }
21763        if let Some(timestamp) = &e.timestamp {
21764            if !first {
21765                self.write(", ");
21766            }
21767            self.generate_expression(timestamp)?;
21768        }
21769        self.write(")");
21770        Ok(())
21771    }
21772
21773    fn generate_convert_to_charset(&mut self, e: &ConvertToCharset) -> Result<()> {
21774        // CONVERT(this USING dest)
21775        self.write_keyword("CONVERT");
21776        self.write("(");
21777        self.generate_expression(&e.this)?;
21778        if let Some(dest) = &e.dest {
21779            self.write_space();
21780            self.write_keyword("USING");
21781            self.write_space();
21782            self.generate_expression(dest)?;
21783        }
21784        self.write(")");
21785        Ok(())
21786    }
21787
21788    fn generate_copy(&mut self, e: &CopyStmt) -> Result<()> {
21789        self.write_keyword("COPY");
21790        if e.is_into {
21791            self.write_space();
21792            self.write_keyword("INTO");
21793        }
21794        self.write_space();
21795
21796        // Generate target table or query (or stage for COPY INTO @stage)
21797        if let Expression::Literal(Literal::String(s)) = &e.this {
21798            if s.starts_with('@') {
21799                self.write(s);
21800            } else {
21801                self.generate_expression(&e.this)?;
21802            }
21803        } else {
21804            self.generate_expression(&e.this)?;
21805        }
21806
21807        // FROM or TO based on kind
21808        if e.kind {
21809            // kind=true means FROM (loading into table)
21810            if self.config.pretty {
21811                self.write_newline();
21812            } else {
21813                self.write_space();
21814            }
21815            self.write_keyword("FROM");
21816            self.write_space();
21817        } else if !e.files.is_empty() {
21818            // kind=false means TO (exporting)
21819            if self.config.pretty {
21820                self.write_newline();
21821            } else {
21822                self.write_space();
21823            }
21824            self.write_keyword("TO");
21825            self.write_space();
21826        }
21827
21828        // Generate source/destination files
21829        for (i, file) in e.files.iter().enumerate() {
21830            if i > 0 {
21831                self.write_space();
21832            }
21833            // For stage references (strings starting with @), output without quotes
21834            if let Expression::Literal(Literal::String(s)) = file {
21835                if s.starts_with('@') {
21836                    self.write(s);
21837                } else {
21838                    self.generate_expression(file)?;
21839                }
21840            } else if let Expression::Identifier(id) = file {
21841                // Backtick-quoted file path (Databricks style: `s3://link`)
21842                if id.quoted {
21843                    self.write("`");
21844                    self.write(&id.name);
21845                    self.write("`");
21846                } else {
21847                    self.generate_expression(file)?;
21848                }
21849            } else {
21850                self.generate_expression(file)?;
21851            }
21852        }
21853
21854        // Generate credentials if present (Snowflake style - not wrapped in WITH)
21855        if !e.with_wrapped {
21856            if let Some(ref creds) = e.credentials {
21857                if let Some(ref storage) = creds.storage {
21858                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21859                    self.write_keyword("STORAGE_INTEGRATION");
21860                    self.write(" = ");
21861                    self.write(storage);
21862                }
21863                if creds.credentials.is_empty() {
21864                    // Empty credentials: CREDENTIALS = ()
21865                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21866                    self.write_keyword("CREDENTIALS");
21867                    self.write(" = ()");
21868                } else {
21869                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21870                    self.write_keyword("CREDENTIALS");
21871                    // Check if this is Redshift-style (single value with empty key)
21872                    // vs Snowflake-style (multiple key=value pairs)
21873                    if creds.credentials.len() == 1 && creds.credentials[0].0.is_empty() {
21874                        // Redshift style: CREDENTIALS 'value'
21875                        self.write(" '");
21876                        self.write(&creds.credentials[0].1);
21877                        self.write("'");
21878                    } else {
21879                        // Snowflake style: CREDENTIALS = (KEY='value' ...)
21880                        self.write(" = (");
21881                        for (i, (k, v)) in creds.credentials.iter().enumerate() {
21882                            if i > 0 {
21883                                self.write_space();
21884                            }
21885                            self.write(k);
21886                            self.write("='");
21887                            self.write(v);
21888                            self.write("'");
21889                        }
21890                        self.write(")");
21891                    }
21892                }
21893                if let Some(ref encryption) = creds.encryption {
21894                    self.write_space();
21895                    self.write_keyword("ENCRYPTION");
21896                    self.write(" = ");
21897                    self.write(encryption);
21898                }
21899            }
21900        }
21901
21902        // Generate parameters
21903        if !e.params.is_empty() {
21904            if e.with_wrapped {
21905                // DuckDB/PostgreSQL/TSQL WITH (...) format
21906                self.write_space();
21907                self.write_keyword("WITH");
21908                self.write(" (");
21909                for (i, param) in e.params.iter().enumerate() {
21910                    if i > 0 {
21911                        self.write(", ");
21912                    }
21913                    self.generate_copy_param_with_format(param)?;
21914                }
21915                self.write(")");
21916            } else {
21917                // Snowflake/Redshift format: KEY = VALUE or KEY VALUE (space separated, no WITH wrapper)
21918                // For Redshift: IAM_ROLE value, CREDENTIALS 'value', REGION 'value', FORMAT type
21919                // For Snowflake: KEY = VALUE
21920                for param in &e.params {
21921                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21922                    // Preserve original case of parameter name (important for Redshift COPY options)
21923                    self.write(&param.name);
21924                    if let Some(ref value) = param.value {
21925                        // Use = only if it was present in the original (param.eq)
21926                        if param.eq {
21927                            self.write(" = ");
21928                        } else {
21929                            self.write(" ");
21930                        }
21931                        if !param.values.is_empty() {
21932                            self.write("(");
21933                            for (i, v) in param.values.iter().enumerate() {
21934                                if i > 0 {
21935                                    self.write_space();
21936                                }
21937                                self.generate_copy_nested_param(v)?;
21938                            }
21939                            self.write(")");
21940                        } else {
21941                            // For COPY parameter values, output identifiers without quoting
21942                            self.generate_copy_param_value(value)?;
21943                        }
21944                    } else if !param.values.is_empty() {
21945                        // For varlen options like FORMAT_OPTIONS, COPY_OPTIONS - no = before (
21946                        if param.eq {
21947                            self.write(" = (");
21948                        } else {
21949                            self.write(" (");
21950                        }
21951                        // Determine separator for values inside parentheses:
21952                        // - Snowflake FILE_FORMAT = (TYPE=CSV FIELD_DELIMITER='|') → space-separated (has = before parens)
21953                        // - Databricks FORMAT_OPTIONS ('opt1'='true', 'opt2'='test') → comma-separated (no = before parens)
21954                        // - Simple value lists like FILES = ('file1', 'file2') → comma-separated
21955                        let is_key_value_pairs = param.values.first().map_or(false, |v| matches!(v, Expression::Eq(_)));
21956                        let sep = if is_key_value_pairs && param.eq { " " } else { ", " };
21957                        for (i, v) in param.values.iter().enumerate() {
21958                            if i > 0 {
21959                                self.write(sep);
21960                            }
21961                            self.generate_copy_nested_param(v)?;
21962                        }
21963                        self.write(")");
21964                    }
21965                }
21966            }
21967        }
21968
21969        Ok(())
21970    }
21971
21972    /// Generate a COPY parameter in WITH (...) format
21973    /// Handles both KEY = VALUE (TSQL) and KEY VALUE (DuckDB/PostgreSQL) formats
21974    fn generate_copy_param_with_format(&mut self, param: &CopyParameter) -> Result<()> {
21975        self.write_keyword(&param.name);
21976        if !param.values.is_empty() {
21977            // Nested values: CREDENTIAL = (IDENTITY='...', SECRET='...')
21978            self.write(" = (");
21979            for (i, v) in param.values.iter().enumerate() {
21980                if i > 0 {
21981                    self.write(", ");
21982                }
21983                self.generate_copy_nested_param(v)?;
21984            }
21985            self.write(")");
21986        } else if let Some(ref value) = param.value {
21987            if param.eq {
21988                self.write(" = ");
21989            } else {
21990                self.write(" ");
21991            }
21992            self.generate_expression(value)?;
21993        }
21994        Ok(())
21995    }
21996
21997    /// Generate nested parameter for COPY statements (KEY=VALUE without spaces)
21998    fn generate_copy_nested_param(&mut self, expr: &Expression) -> Result<()> {
21999        match expr {
22000            Expression::Eq(eq) => {
22001                // Generate key
22002                match &eq.left {
22003                    Expression::Column(c) => self.write(&c.name.name),
22004                    _ => self.generate_expression(&eq.left)?,
22005                }
22006                self.write("=");
22007                // Generate value
22008                match &eq.right {
22009                    Expression::Literal(Literal::String(s)) => {
22010                        self.write("'");
22011                        self.write(s);
22012                        self.write("'");
22013                    }
22014                    Expression::Tuple(t) => {
22015                        // For lists like NULL_IF=('', 'str1')
22016                        self.write("(");
22017                        if self.config.pretty {
22018                            self.write_newline();
22019                            self.indent_level += 1;
22020                            for (i, item) in t.expressions.iter().enumerate() {
22021                                if i > 0 {
22022                                    self.write(", ");
22023                                }
22024                                self.write_indent();
22025                                self.generate_expression(item)?;
22026                            }
22027                            self.write_newline();
22028                            self.indent_level -= 1;
22029                        } else {
22030                            for (i, item) in t.expressions.iter().enumerate() {
22031                                if i > 0 {
22032                                    self.write(", ");
22033                                }
22034                                self.generate_expression(item)?;
22035                            }
22036                        }
22037                        self.write(")");
22038                    }
22039                    _ => self.generate_expression(&eq.right)?,
22040                }
22041                Ok(())
22042            }
22043            Expression::Column(c) => {
22044                // Standalone keyword like COMPRESSION
22045                self.write(&c.name.name);
22046                Ok(())
22047            }
22048            _ => self.generate_expression(expr),
22049        }
22050    }
22051
22052    /// Generate a COPY parameter value, outputting identifiers/columns without quoting
22053    /// This is needed for Redshift-style COPY params like: IAM_ROLE default, FORMAT orc
22054    fn generate_copy_param_value(&mut self, expr: &Expression) -> Result<()> {
22055        match expr {
22056            Expression::Column(c) => {
22057                // Output identifier, preserving quotes if originally quoted
22058                if c.name.quoted {
22059                    self.write("\"");
22060                    self.write(&c.name.name);
22061                    self.write("\"");
22062                } else {
22063                    self.write(&c.name.name);
22064                }
22065                Ok(())
22066            }
22067            Expression::Identifier(id) => {
22068                // Output identifier, preserving quotes if originally quoted
22069                if id.quoted {
22070                    self.write("\"");
22071                    self.write(&id.name);
22072                    self.write("\"");
22073                } else {
22074                    self.write(&id.name);
22075                }
22076                Ok(())
22077            }
22078            Expression::Literal(Literal::String(s)) => {
22079                // Output string with quotes
22080                self.write("'");
22081                self.write(s);
22082                self.write("'");
22083                Ok(())
22084            }
22085            _ => self.generate_expression(expr),
22086        }
22087    }
22088
22089    fn generate_copy_parameter(&mut self, e: &CopyParameter) -> Result<()> {
22090        self.write_keyword(&e.name);
22091        if let Some(ref value) = e.value {
22092            if e.eq {
22093                self.write(" = ");
22094            } else {
22095                self.write(" ");
22096            }
22097            self.generate_expression(value)?;
22098        }
22099        if !e.values.is_empty() {
22100            if e.eq {
22101                self.write(" = ");
22102            } else {
22103                self.write(" ");
22104            }
22105            self.write("(");
22106            for (i, v) in e.values.iter().enumerate() {
22107                if i > 0 {
22108                    self.write(", ");
22109                }
22110                self.generate_expression(v)?;
22111            }
22112            self.write(")");
22113        }
22114        Ok(())
22115    }
22116
22117    fn generate_corr(&mut self, e: &Corr) -> Result<()> {
22118        // CORR(this, expression)
22119        self.write_keyword("CORR");
22120        self.write("(");
22121        self.generate_expression(&e.this)?;
22122        self.write(", ");
22123        self.generate_expression(&e.expression)?;
22124        self.write(")");
22125        Ok(())
22126    }
22127
22128    fn generate_cosine_distance(&mut self, e: &CosineDistance) -> Result<()> {
22129        // COSINE_DISTANCE(this, expression)
22130        self.write_keyword("COSINE_DISTANCE");
22131        self.write("(");
22132        self.generate_expression(&e.this)?;
22133        self.write(", ");
22134        self.generate_expression(&e.expression)?;
22135        self.write(")");
22136        Ok(())
22137    }
22138
22139    fn generate_covar_pop(&mut self, e: &CovarPop) -> Result<()> {
22140        // COVAR_POP(this, expression)
22141        self.write_keyword("COVAR_POP");
22142        self.write("(");
22143        self.generate_expression(&e.this)?;
22144        self.write(", ");
22145        self.generate_expression(&e.expression)?;
22146        self.write(")");
22147        Ok(())
22148    }
22149
22150    fn generate_covar_samp(&mut self, e: &CovarSamp) -> Result<()> {
22151        // COVAR_SAMP(this, expression)
22152        self.write_keyword("COVAR_SAMP");
22153        self.write("(");
22154        self.generate_expression(&e.this)?;
22155        self.write(", ");
22156        self.generate_expression(&e.expression)?;
22157        self.write(")");
22158        Ok(())
22159    }
22160
22161    fn generate_credentials(&mut self, e: &Credentials) -> Result<()> {
22162        // CREDENTIALS (key1='value1', key2='value2')
22163        self.write_keyword("CREDENTIALS");
22164        self.write(" (");
22165        for (i, (key, value)) in e.credentials.iter().enumerate() {
22166            if i > 0 {
22167                self.write(", ");
22168            }
22169            self.write(key);
22170            self.write("='");
22171            self.write(value);
22172            self.write("'");
22173        }
22174        self.write(")");
22175        Ok(())
22176    }
22177
22178    fn generate_credentials_property(&mut self, e: &CredentialsProperty) -> Result<()> {
22179        // CREDENTIALS=(expressions)
22180        self.write_keyword("CREDENTIALS");
22181        self.write("=(");
22182        for (i, expr) in e.expressions.iter().enumerate() {
22183            if i > 0 {
22184                self.write(", ");
22185            }
22186            self.generate_expression(expr)?;
22187        }
22188        self.write(")");
22189        Ok(())
22190    }
22191
22192    fn generate_cte(&mut self, e: &Cte) -> Result<()> {
22193        use crate::dialects::DialectType;
22194
22195        // Python: return f"{alias_sql}{key_expressions} AS {materialized or ''}{self.wrap(expression)}"
22196        // Output: alias [(col1, col2, ...)] AS [MATERIALIZED|NOT MATERIALIZED] (subquery)
22197        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) && !e.alias_first {
22198            self.generate_expression(&e.this)?;
22199            self.write_space();
22200            self.write_keyword("AS");
22201            self.write_space();
22202            self.generate_identifier(&e.alias)?;
22203            return Ok(());
22204        }
22205        self.write(&e.alias.name);
22206
22207        // BigQuery doesn't support column aliases in CTE definitions
22208        let skip_cte_columns = matches!(self.config.dialect, Some(DialectType::BigQuery));
22209
22210        if !e.columns.is_empty() && !skip_cte_columns {
22211            self.write("(");
22212            for (i, col) in e.columns.iter().enumerate() {
22213                if i > 0 {
22214                    self.write(", ");
22215                }
22216                self.write(&col.name);
22217            }
22218            self.write(")");
22219        }
22220        // USING KEY (columns) for DuckDB recursive CTEs
22221        if !e.key_expressions.is_empty() {
22222            self.write_space();
22223            self.write_keyword("USING KEY");
22224            self.write(" (");
22225            for (i, key) in e.key_expressions.iter().enumerate() {
22226                if i > 0 {
22227                    self.write(", ");
22228                }
22229                self.write(&key.name);
22230            }
22231            self.write(")");
22232        }
22233        self.write_space();
22234        self.write_keyword("AS");
22235        self.write_space();
22236        if let Some(materialized) = e.materialized {
22237            if materialized {
22238                self.write_keyword("MATERIALIZED");
22239            } else {
22240                self.write_keyword("NOT MATERIALIZED");
22241            }
22242            self.write_space();
22243        }
22244        self.write("(");
22245        self.generate_expression(&e.this)?;
22246        self.write(")");
22247        Ok(())
22248    }
22249
22250    fn generate_cube(&mut self, e: &Cube) -> Result<()> {
22251        // Python: return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
22252        if e.expressions.is_empty() {
22253            self.write_keyword("WITH CUBE");
22254        } else {
22255            self.write_keyword("CUBE");
22256            self.write("(");
22257            for (i, expr) in e.expressions.iter().enumerate() {
22258                if i > 0 {
22259                    self.write(", ");
22260                }
22261                self.generate_expression(expr)?;
22262            }
22263            self.write(")");
22264        }
22265        Ok(())
22266    }
22267
22268    fn generate_current_datetime(&mut self, e: &CurrentDatetime) -> Result<()> {
22269        // CURRENT_DATETIME or CURRENT_DATETIME(timezone)
22270        self.write_keyword("CURRENT_DATETIME");
22271        if let Some(this) = &e.this {
22272            self.write("(");
22273            self.generate_expression(this)?;
22274            self.write(")");
22275        }
22276        Ok(())
22277    }
22278
22279    fn generate_current_schema(&mut self, _e: &CurrentSchema) -> Result<()> {
22280        // CURRENT_SCHEMA - no arguments
22281        self.write_keyword("CURRENT_SCHEMA");
22282        Ok(())
22283    }
22284
22285    fn generate_current_schemas(&mut self, e: &CurrentSchemas) -> Result<()> {
22286        // CURRENT_SCHEMAS(include_implicit)
22287        self.write_keyword("CURRENT_SCHEMAS");
22288        self.write("(");
22289        if let Some(this) = &e.this {
22290            self.generate_expression(this)?;
22291        }
22292        self.write(")");
22293        Ok(())
22294    }
22295
22296    fn generate_current_user(&mut self, e: &CurrentUser) -> Result<()> {
22297        // CURRENT_USER or CURRENT_USER()
22298        self.write_keyword("CURRENT_USER");
22299        // Some dialects always need parens: Snowflake, Spark, Hive, DuckDB, BigQuery, MySQL, Databricks
22300        let needs_parens = e.this.is_some() || matches!(
22301            self.config.dialect,
22302            Some(DialectType::Snowflake) | Some(DialectType::Spark)
22303            | Some(DialectType::Hive) | Some(DialectType::DuckDB) | Some(DialectType::BigQuery)
22304            | Some(DialectType::MySQL) | Some(DialectType::Databricks)
22305        );
22306        if needs_parens {
22307            self.write("()");
22308        }
22309        Ok(())
22310    }
22311
22312    fn generate_d_pipe(&mut self, e: &DPipe) -> Result<()> {
22313        // In Solr, || is OR, not string concatenation (DPIPE_IS_STRING_CONCAT = False)
22314        if self.config.dialect == Some(DialectType::Solr) {
22315            self.generate_expression(&e.this)?;
22316            self.write(" ");
22317            self.write_keyword("OR");
22318            self.write(" ");
22319            self.generate_expression(&e.expression)?;
22320        } else {
22321            // String concatenation: this || expression
22322            self.generate_expression(&e.this)?;
22323            self.write(" || ");
22324            self.generate_expression(&e.expression)?;
22325        }
22326        Ok(())
22327    }
22328
22329    fn generate_data_blocksize_property(&mut self, e: &DataBlocksizeProperty) -> Result<()> {
22330        // DATABLOCKSIZE=... (Teradata)
22331        self.write_keyword("DATABLOCKSIZE");
22332        self.write("=");
22333        if let Some(size) = e.size {
22334            self.write(&size.to_string());
22335            if let Some(units) = &e.units {
22336                self.write_space();
22337                self.generate_expression(units)?;
22338            }
22339        } else if e.minimum.is_some() {
22340            self.write_keyword("MINIMUM");
22341        } else if e.maximum.is_some() {
22342            self.write_keyword("MAXIMUM");
22343        } else if e.default.is_some() {
22344            self.write_keyword("DEFAULT");
22345        }
22346        Ok(())
22347    }
22348
22349    fn generate_data_deletion_property(&mut self, e: &DataDeletionProperty) -> Result<()> {
22350        // DATA_DELETION=ON or DATA_DELETION=OFF or DATA_DELETION=ON(FILTER_COLUMN=col, RETENTION_PERIOD=...)
22351        self.write_keyword("DATA_DELETION");
22352        self.write("=");
22353
22354        let is_on = matches!(&*e.on, Expression::Boolean(BooleanLiteral { value: true }));
22355        let has_options = e.filter_column.is_some() || e.retention_period.is_some();
22356
22357        if is_on {
22358            self.write_keyword("ON");
22359            if has_options {
22360                self.write("(");
22361                let mut first = true;
22362                if let Some(filter_column) = &e.filter_column {
22363                    self.write_keyword("FILTER_COLUMN");
22364                    self.write("=");
22365                    self.generate_expression(filter_column)?;
22366                    first = false;
22367                }
22368                if let Some(retention_period) = &e.retention_period {
22369                    if !first {
22370                        self.write(", ");
22371                    }
22372                    self.write_keyword("RETENTION_PERIOD");
22373                    self.write("=");
22374                    self.generate_expression(retention_period)?;
22375                }
22376                self.write(")");
22377            }
22378        } else {
22379            self.write_keyword("OFF");
22380        }
22381        Ok(())
22382    }
22383
22384    /// Generate a Date function expression
22385    /// For Exasol: {d'value'} -> TO_DATE('value')
22386    /// For other dialects: DATE('value')
22387    fn generate_date_func(&mut self, e: &UnaryFunc) -> Result<()> {
22388        use crate::dialects::DialectType;
22389        use crate::expressions::Literal;
22390
22391        match self.config.dialect {
22392            // Exasol uses TO_DATE for Date expressions
22393            Some(DialectType::Exasol) => {
22394                self.write_keyword("TO_DATE");
22395                self.write("(");
22396                // Extract the string value from the expression if it's a string literal
22397                match &e.this {
22398                    Expression::Literal(Literal::String(s)) => {
22399                        self.write("'");
22400                        self.write(s);
22401                        self.write("'");
22402                    }
22403                    _ => {
22404                        self.generate_expression(&e.this)?;
22405                    }
22406                }
22407                self.write(")");
22408            }
22409            // Standard: DATE(value)
22410            _ => {
22411                self.write_keyword("DATE");
22412                self.write("(");
22413                self.generate_expression(&e.this)?;
22414                self.write(")");
22415            }
22416        }
22417        Ok(())
22418    }
22419
22420    fn generate_date_bin(&mut self, e: &DateBin) -> Result<()> {
22421        // DATE_BIN(interval, timestamp[, origin])
22422        self.write_keyword("DATE_BIN");
22423        self.write("(");
22424        self.generate_expression(&e.this)?;
22425        self.write(", ");
22426        self.generate_expression(&e.expression)?;
22427        if let Some(origin) = &e.origin {
22428            self.write(", ");
22429            self.generate_expression(origin)?;
22430        }
22431        self.write(")");
22432        Ok(())
22433    }
22434
22435    fn generate_date_format_column_constraint(&mut self, e: &DateFormatColumnConstraint) -> Result<()> {
22436        // FORMAT 'format_string' (Teradata)
22437        self.write_keyword("FORMAT");
22438        self.write_space();
22439        self.generate_expression(&e.this)?;
22440        Ok(())
22441    }
22442
22443    fn generate_date_from_parts(&mut self, e: &DateFromParts) -> Result<()> {
22444        // DATE_FROM_PARTS(year, month, day) or DATEFROMPARTS(year, month, day)
22445        self.write_keyword("DATE_FROM_PARTS");
22446        self.write("(");
22447        let mut first = true;
22448        if let Some(year) = &e.year {
22449            self.generate_expression(year)?;
22450            first = false;
22451        }
22452        if let Some(month) = &e.month {
22453            if !first {
22454                self.write(", ");
22455            }
22456            self.generate_expression(month)?;
22457            first = false;
22458        }
22459        if let Some(day) = &e.day {
22460            if !first {
22461                self.write(", ");
22462            }
22463            self.generate_expression(day)?;
22464        }
22465        self.write(")");
22466        Ok(())
22467    }
22468
22469    fn generate_datetime(&mut self, e: &Datetime) -> Result<()> {
22470        // DATETIME(this) or DATETIME(this, expression)
22471        self.write_keyword("DATETIME");
22472        self.write("(");
22473        self.generate_expression(&e.this)?;
22474        if let Some(expr) = &e.expression {
22475            self.write(", ");
22476            self.generate_expression(expr)?;
22477        }
22478        self.write(")");
22479        Ok(())
22480    }
22481
22482    fn generate_datetime_add(&mut self, e: &DatetimeAdd) -> Result<()> {
22483        // DATETIME_ADD(this, expression, unit)
22484        self.write_keyword("DATETIME_ADD");
22485        self.write("(");
22486        self.generate_expression(&e.this)?;
22487        self.write(", ");
22488        self.generate_expression(&e.expression)?;
22489        if let Some(unit) = &e.unit {
22490            self.write(", ");
22491            self.write_keyword(unit);
22492        }
22493        self.write(")");
22494        Ok(())
22495    }
22496
22497    fn generate_datetime_diff(&mut self, e: &DatetimeDiff) -> Result<()> {
22498        // DATETIME_DIFF(this, expression, unit)
22499        self.write_keyword("DATETIME_DIFF");
22500        self.write("(");
22501        self.generate_expression(&e.this)?;
22502        self.write(", ");
22503        self.generate_expression(&e.expression)?;
22504        if let Some(unit) = &e.unit {
22505            self.write(", ");
22506            self.write_keyword(unit);
22507        }
22508        self.write(")");
22509        Ok(())
22510    }
22511
22512    fn generate_datetime_sub(&mut self, e: &DatetimeSub) -> Result<()> {
22513        // DATETIME_SUB(this, expression, unit)
22514        self.write_keyword("DATETIME_SUB");
22515        self.write("(");
22516        self.generate_expression(&e.this)?;
22517        self.write(", ");
22518        self.generate_expression(&e.expression)?;
22519        if let Some(unit) = &e.unit {
22520            self.write(", ");
22521            self.write_keyword(unit);
22522        }
22523        self.write(")");
22524        Ok(())
22525    }
22526
22527    fn generate_datetime_trunc(&mut self, e: &DatetimeTrunc) -> Result<()> {
22528        // DATETIME_TRUNC(this, unit, zone)
22529        self.write_keyword("DATETIME_TRUNC");
22530        self.write("(");
22531        self.generate_expression(&e.this)?;
22532        self.write(", ");
22533        self.write_keyword(&e.unit);
22534        if let Some(zone) = &e.zone {
22535            self.write(", ");
22536            self.generate_expression(zone)?;
22537        }
22538        self.write(")");
22539        Ok(())
22540    }
22541
22542    fn generate_dayname(&mut self, e: &Dayname) -> Result<()> {
22543        // DAYNAME(this)
22544        self.write_keyword("DAYNAME");
22545        self.write("(");
22546        self.generate_expression(&e.this)?;
22547        self.write(")");
22548        Ok(())
22549    }
22550
22551    fn generate_declare(&mut self, e: &Declare) -> Result<()> {
22552        // DECLARE var1 AS type1, var2 AS type2, ...
22553        self.write_keyword("DECLARE");
22554        self.write_space();
22555        for (i, expr) in e.expressions.iter().enumerate() {
22556            if i > 0 {
22557                self.write(", ");
22558            }
22559            self.generate_expression(expr)?;
22560        }
22561        Ok(())
22562    }
22563
22564    fn generate_declare_item(&mut self, e: &DeclareItem) -> Result<()> {
22565        use crate::dialects::DialectType;
22566
22567        // variable TYPE [DEFAULT default]
22568        self.generate_expression(&e.this)?;
22569        // BigQuery multi-variable: DECLARE X, Y, Z INT64
22570        for name in &e.additional_names {
22571            self.write(", ");
22572            self.generate_expression(name)?;
22573        }
22574        if let Some(kind) = &e.kind {
22575            self.write_space();
22576            // BigQuery uses: DECLARE x INT64 DEFAULT value (no AS)
22577            // TSQL: Always includes AS (normalization)
22578            // Others: Include AS if present in original
22579            match self.config.dialect {
22580                Some(DialectType::BigQuery) => {
22581                    self.write(kind);
22582                }
22583                Some(DialectType::TSQL) => {
22584                    // TSQL: Check for complex TABLE constraints that should be passed through unchanged
22585                    // Python sqlglot falls back to Command for TABLE declarations with CLUSTERED,
22586                    // NONCLUSTERED, or INDEX constraints
22587                    let is_complex_table = kind.starts_with("TABLE") &&
22588                        (kind.contains("CLUSTERED") || kind.contains("INDEX"));
22589
22590                    if is_complex_table {
22591                        // Complex TABLE declarations: preserve as-is (no AS, no INT normalization)
22592                        self.write(kind);
22593                    } else {
22594                        // Simple declarations: add AS (except for CURSOR) and normalize INT
22595                        if !kind.starts_with("CURSOR") {
22596                            self.write_keyword("AS");
22597                            self.write_space();
22598                        }
22599                        // Normalize INT to INTEGER for TSQL DECLARE statements
22600                        if kind == "INT" {
22601                            self.write("INTEGER");
22602                        } else if kind.starts_with("TABLE") {
22603                            // Normalize INT to INTEGER inside TABLE column definitions
22604                            let normalized = kind
22605                                .replace(" INT ", " INTEGER ")
22606                                .replace(" INT,", " INTEGER,")
22607                                .replace(" INT)", " INTEGER)")
22608                                .replace("(INT ", "(INTEGER ");
22609                            self.write(&normalized);
22610                        } else {
22611                            self.write(kind);
22612                        }
22613                    }
22614                }
22615                _ => {
22616                    if e.has_as {
22617                        self.write_keyword("AS");
22618                        self.write_space();
22619                    }
22620                    self.write(kind);
22621                }
22622            }
22623        }
22624        if let Some(default) = &e.default {
22625            // BigQuery uses DEFAULT, others use =
22626            match self.config.dialect {
22627                Some(DialectType::BigQuery) => {
22628                    self.write_space();
22629                    self.write_keyword("DEFAULT");
22630                    self.write_space();
22631                }
22632                _ => {
22633                    self.write(" = ");
22634                }
22635            }
22636            self.generate_expression(default)?;
22637        }
22638        Ok(())
22639    }
22640
22641    fn generate_decode_case(&mut self, e: &DecodeCase) -> Result<()> {
22642        // DECODE(expr, search1, result1, search2, result2, ..., default)
22643        self.write_keyword("DECODE");
22644        self.write("(");
22645        for (i, expr) in e.expressions.iter().enumerate() {
22646            if i > 0 {
22647                self.write(", ");
22648            }
22649            self.generate_expression(expr)?;
22650        }
22651        self.write(")");
22652        Ok(())
22653    }
22654
22655    fn generate_decompress_binary(&mut self, e: &DecompressBinary) -> Result<()> {
22656        // DECOMPRESS(expr, 'method')
22657        self.write_keyword("DECOMPRESS");
22658        self.write("(");
22659        self.generate_expression(&e.this)?;
22660        self.write(", '");
22661        self.write(&e.method);
22662        self.write("')");
22663        Ok(())
22664    }
22665
22666    fn generate_decompress_string(&mut self, e: &DecompressString) -> Result<()> {
22667        // DECOMPRESS(expr, 'method')
22668        self.write_keyword("DECOMPRESS");
22669        self.write("(");
22670        self.generate_expression(&e.this)?;
22671        self.write(", '");
22672        self.write(&e.method);
22673        self.write("')");
22674        Ok(())
22675    }
22676
22677    fn generate_decrypt(&mut self, e: &Decrypt) -> Result<()> {
22678        // DECRYPT(value, passphrase [, aad [, algorithm]])
22679        self.write_keyword("DECRYPT");
22680        self.write("(");
22681        self.generate_expression(&e.this)?;
22682        if let Some(passphrase) = &e.passphrase {
22683            self.write(", ");
22684            self.generate_expression(passphrase)?;
22685        }
22686        if let Some(aad) = &e.aad {
22687            self.write(", ");
22688            self.generate_expression(aad)?;
22689        }
22690        if let Some(method) = &e.encryption_method {
22691            self.write(", ");
22692            self.generate_expression(method)?;
22693        }
22694        self.write(")");
22695        Ok(())
22696    }
22697
22698    fn generate_decrypt_raw(&mut self, e: &DecryptRaw) -> Result<()> {
22699        // DECRYPT_RAW(value, key [, iv [, aad [, algorithm]]])
22700        self.write_keyword("DECRYPT_RAW");
22701        self.write("(");
22702        self.generate_expression(&e.this)?;
22703        if let Some(key) = &e.key {
22704            self.write(", ");
22705            self.generate_expression(key)?;
22706        }
22707        if let Some(iv) = &e.iv {
22708            self.write(", ");
22709            self.generate_expression(iv)?;
22710        }
22711        if let Some(aad) = &e.aad {
22712            self.write(", ");
22713            self.generate_expression(aad)?;
22714        }
22715        if let Some(method) = &e.encryption_method {
22716            self.write(", ");
22717            self.generate_expression(method)?;
22718        }
22719        self.write(")");
22720        Ok(())
22721    }
22722
22723    fn generate_definer_property(&mut self, e: &DefinerProperty) -> Result<()> {
22724        // DEFINER = user
22725        self.write_keyword("DEFINER");
22726        self.write(" = ");
22727        self.generate_expression(&e.this)?;
22728        Ok(())
22729    }
22730
22731    fn generate_detach(&mut self, e: &Detach) -> Result<()> {
22732        // Python: DETACH[DATABASE IF EXISTS] this
22733        self.write_keyword("DETACH");
22734        if e.exists {
22735            self.write_keyword(" DATABASE IF EXISTS");
22736        }
22737        self.write_space();
22738        self.generate_expression(&e.this)?;
22739        Ok(())
22740    }
22741
22742    fn generate_dict_property(&mut self, e: &DictProperty) -> Result<()> {
22743        let property_name = match e.this.as_ref() {
22744            Expression::Identifier(id) => id.name.as_str(),
22745            Expression::Var(v) => v.this.as_str(),
22746            _ => "DICTIONARY",
22747        };
22748        self.write_keyword(property_name);
22749        self.write("(");
22750        self.write(&e.kind);
22751        if let Some(settings) = &e.settings {
22752            self.write("(");
22753            if let Expression::Tuple(t) = settings.as_ref() {
22754                if self.config.pretty && !t.expressions.is_empty() {
22755                    self.write_newline();
22756                    self.indent_level += 1;
22757                    for (i, pair) in t.expressions.iter().enumerate() {
22758                        if i > 0 {
22759                            self.write(",");
22760                            self.write_newline();
22761                        }
22762                        self.write_indent();
22763                        if let Expression::Tuple(pair_tuple) = pair {
22764                            if let Some(k) = pair_tuple.expressions.first() {
22765                                self.generate_expression(k)?;
22766                            }
22767                            if let Some(v) = pair_tuple.expressions.get(1) {
22768                                self.write(" ");
22769                                self.generate_expression(v)?;
22770                            }
22771                        } else {
22772                            self.generate_expression(pair)?;
22773                        }
22774                    }
22775                    self.indent_level -= 1;
22776                    self.write_newline();
22777                    self.write_indent();
22778                } else {
22779                    for (i, pair) in t.expressions.iter().enumerate() {
22780                        if i > 0 {
22781                            self.write(", ");
22782                        }
22783                        if let Expression::Tuple(pair_tuple) = pair {
22784                            if let Some(k) = pair_tuple.expressions.first() {
22785                                self.generate_expression(k)?;
22786                            }
22787                            if let Some(v) = pair_tuple.expressions.get(1) {
22788                                self.write(" ");
22789                                self.generate_expression(v)?;
22790                            }
22791                        } else {
22792                            self.generate_expression(pair)?;
22793                        }
22794                    }
22795                }
22796            } else {
22797                self.generate_expression(settings)?;
22798            }
22799            self.write(")");
22800        } else if property_name.eq_ignore_ascii_case("LAYOUT") {
22801            self.write("()");
22802        }
22803        self.write(")");
22804        Ok(())
22805    }
22806
22807    fn generate_dict_range(&mut self, e: &DictRange) -> Result<()> {
22808        let property_name = match e.this.as_ref() {
22809            Expression::Identifier(id) => id.name.as_str(),
22810            Expression::Var(v) => v.this.as_str(),
22811            _ => "RANGE",
22812        };
22813        self.write_keyword(property_name);
22814        self.write("(");
22815        if let Some(min) = &e.min {
22816            self.write_keyword("MIN");
22817            self.write_space();
22818            self.generate_expression(min)?;
22819        }
22820        if let Some(max) = &e.max {
22821            self.write_space();
22822            self.write_keyword("MAX");
22823            self.write_space();
22824            self.generate_expression(max)?;
22825        }
22826        self.write(")");
22827        Ok(())
22828    }
22829
22830    fn generate_directory(&mut self, e: &Directory) -> Result<()> {
22831        // Python: {local}DIRECTORY {this}{row_format}
22832        if e.local.is_some() {
22833            self.write_keyword("LOCAL ");
22834        }
22835        self.write_keyword("DIRECTORY");
22836        self.write_space();
22837        self.generate_expression(&e.this)?;
22838        if let Some(row_format) = &e.row_format {
22839            self.write_space();
22840            self.generate_expression(row_format)?;
22841        }
22842        Ok(())
22843    }
22844
22845    fn generate_dist_key_property(&mut self, e: &DistKeyProperty) -> Result<()> {
22846        // Redshift: DISTKEY(column)
22847        self.write_keyword("DISTKEY");
22848        self.write("(");
22849        self.generate_expression(&e.this)?;
22850        self.write(")");
22851        Ok(())
22852    }
22853
22854    fn generate_dist_style_property(&mut self, e: &DistStyleProperty) -> Result<()> {
22855        // Redshift: DISTSTYLE KEY|ALL|EVEN|AUTO
22856        self.write_keyword("DISTSTYLE");
22857        self.write_space();
22858        self.generate_expression(&e.this)?;
22859        Ok(())
22860    }
22861
22862    fn generate_distribute_by(&mut self, e: &DistributeBy) -> Result<()> {
22863        // Python: "DISTRIBUTE BY" expressions
22864        self.write_keyword("DISTRIBUTE BY");
22865        self.write_space();
22866        for (i, expr) in e.expressions.iter().enumerate() {
22867            if i > 0 {
22868                self.write(", ");
22869            }
22870            self.generate_expression(expr)?;
22871        }
22872        Ok(())
22873    }
22874
22875    fn generate_distributed_by_property(&mut self, e: &DistributedByProperty) -> Result<()> {
22876        // Python: DISTRIBUTED BY kind (expressions) BUCKETS buckets order
22877        self.write_keyword("DISTRIBUTED BY");
22878        self.write_space();
22879        self.write(&e.kind);
22880        if !e.expressions.is_empty() {
22881            self.write(" (");
22882            for (i, expr) in e.expressions.iter().enumerate() {
22883                if i > 0 {
22884                    self.write(", ");
22885                }
22886                self.generate_expression(expr)?;
22887            }
22888            self.write(")");
22889        }
22890        if let Some(buckets) = &e.buckets {
22891            self.write_space();
22892            self.write_keyword("BUCKETS");
22893            self.write_space();
22894            self.generate_expression(buckets)?;
22895        }
22896        if let Some(order) = &e.order {
22897            self.write_space();
22898            self.generate_expression(order)?;
22899        }
22900        Ok(())
22901    }
22902
22903    fn generate_dot_product(&mut self, e: &DotProduct) -> Result<()> {
22904        // DOT_PRODUCT(vector1, vector2)
22905        self.write_keyword("DOT_PRODUCT");
22906        self.write("(");
22907        self.generate_expression(&e.this)?;
22908        self.write(", ");
22909        self.generate_expression(&e.expression)?;
22910        self.write(")");
22911        Ok(())
22912    }
22913
22914    fn generate_drop_partition(&mut self, e: &DropPartition) -> Result<()> {
22915        // Python: DROP{IF EXISTS }expressions
22916        self.write_keyword("DROP");
22917        if e.exists {
22918            self.write_keyword(" IF EXISTS ");
22919        } else {
22920            self.write_space();
22921        }
22922        for (i, expr) in e.expressions.iter().enumerate() {
22923            if i > 0 {
22924                self.write(", ");
22925            }
22926            self.generate_expression(expr)?;
22927        }
22928        Ok(())
22929    }
22930
22931    fn generate_duplicate_key_property(&mut self, e: &DuplicateKeyProperty) -> Result<()> {
22932        // Python: DUPLICATE KEY (expressions)
22933        self.write_keyword("DUPLICATE KEY");
22934        self.write(" (");
22935        for (i, expr) in e.expressions.iter().enumerate() {
22936            if i > 0 {
22937                self.write(", ");
22938            }
22939            self.generate_expression(expr)?;
22940        }
22941        self.write(")");
22942        Ok(())
22943    }
22944
22945    fn generate_elt(&mut self, e: &Elt) -> Result<()> {
22946        // ELT(index, str1, str2, ...)
22947        self.write_keyword("ELT");
22948        self.write("(");
22949        self.generate_expression(&e.this)?;
22950        for expr in &e.expressions {
22951            self.write(", ");
22952            self.generate_expression(expr)?;
22953        }
22954        self.write(")");
22955        Ok(())
22956    }
22957
22958    fn generate_encode(&mut self, e: &Encode) -> Result<()> {
22959        // ENCODE(string, charset)
22960        self.write_keyword("ENCODE");
22961        self.write("(");
22962        self.generate_expression(&e.this)?;
22963        if let Some(charset) = &e.charset {
22964            self.write(", ");
22965            self.generate_expression(charset)?;
22966        }
22967        self.write(")");
22968        Ok(())
22969    }
22970
22971    fn generate_encode_property(&mut self, e: &EncodeProperty) -> Result<()> {
22972        // Python: [KEY ]ENCODE this [properties]
22973        if e.key.is_some() {
22974            self.write_keyword("KEY ");
22975        }
22976        self.write_keyword("ENCODE");
22977        self.write_space();
22978        self.generate_expression(&e.this)?;
22979        if !e.properties.is_empty() {
22980            self.write(" (");
22981            for (i, prop) in e.properties.iter().enumerate() {
22982                if i > 0 {
22983                    self.write(", ");
22984                }
22985                self.generate_expression(prop)?;
22986            }
22987            self.write(")");
22988        }
22989        Ok(())
22990    }
22991
22992    fn generate_encrypt(&mut self, e: &Encrypt) -> Result<()> {
22993        // ENCRYPT(value, passphrase [, aad [, algorithm]])
22994        self.write_keyword("ENCRYPT");
22995        self.write("(");
22996        self.generate_expression(&e.this)?;
22997        if let Some(passphrase) = &e.passphrase {
22998            self.write(", ");
22999            self.generate_expression(passphrase)?;
23000        }
23001        if let Some(aad) = &e.aad {
23002            self.write(", ");
23003            self.generate_expression(aad)?;
23004        }
23005        if let Some(method) = &e.encryption_method {
23006            self.write(", ");
23007            self.generate_expression(method)?;
23008        }
23009        self.write(")");
23010        Ok(())
23011    }
23012
23013    fn generate_encrypt_raw(&mut self, e: &EncryptRaw) -> Result<()> {
23014        // ENCRYPT_RAW(value, key [, iv [, aad [, algorithm]]])
23015        self.write_keyword("ENCRYPT_RAW");
23016        self.write("(");
23017        self.generate_expression(&e.this)?;
23018        if let Some(key) = &e.key {
23019            self.write(", ");
23020            self.generate_expression(key)?;
23021        }
23022        if let Some(iv) = &e.iv {
23023            self.write(", ");
23024            self.generate_expression(iv)?;
23025        }
23026        if let Some(aad) = &e.aad {
23027            self.write(", ");
23028            self.generate_expression(aad)?;
23029        }
23030        if let Some(method) = &e.encryption_method {
23031            self.write(", ");
23032            self.generate_expression(method)?;
23033        }
23034        self.write(")");
23035        Ok(())
23036    }
23037
23038    fn generate_engine_property(&mut self, e: &EngineProperty) -> Result<()> {
23039        // MySQL: ENGINE = InnoDB
23040        self.write_keyword("ENGINE");
23041        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
23042            self.write("=");
23043        } else {
23044            self.write(" = ");
23045        }
23046        self.generate_expression(&e.this)?;
23047        Ok(())
23048    }
23049
23050    fn generate_enviroment_property(&mut self, e: &EnviromentProperty) -> Result<()> {
23051        // ENVIRONMENT (expressions)
23052        self.write_keyword("ENVIRONMENT");
23053        self.write(" (");
23054        for (i, expr) in e.expressions.iter().enumerate() {
23055            if i > 0 {
23056                self.write(", ");
23057            }
23058            self.generate_expression(expr)?;
23059        }
23060        self.write(")");
23061        Ok(())
23062    }
23063
23064    fn generate_ephemeral_column_constraint(&mut self, e: &EphemeralColumnConstraint) -> Result<()> {
23065        // MySQL: EPHEMERAL [expr]
23066        self.write_keyword("EPHEMERAL");
23067        if let Some(this) = &e.this {
23068            self.write_space();
23069            self.generate_expression(this)?;
23070        }
23071        Ok(())
23072    }
23073
23074    fn generate_equal_null(&mut self, e: &EqualNull) -> Result<()> {
23075        // Snowflake: EQUAL_NULL(a, b)
23076        self.write_keyword("EQUAL_NULL");
23077        self.write("(");
23078        self.generate_expression(&e.this)?;
23079        self.write(", ");
23080        self.generate_expression(&e.expression)?;
23081        self.write(")");
23082        Ok(())
23083    }
23084
23085    fn generate_euclidean_distance(&mut self, e: &EuclideanDistance) -> Result<()> {
23086        use crate::dialects::DialectType;
23087
23088        // PostgreSQL uses <-> operator syntax
23089        match self.config.dialect {
23090            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
23091                self.generate_expression(&e.this)?;
23092                self.write(" <-> ");
23093                self.generate_expression(&e.expression)?;
23094            }
23095            _ => {
23096                // Other dialects use EUCLIDEAN_DISTANCE function
23097                self.write_keyword("EUCLIDEAN_DISTANCE");
23098                self.write("(");
23099                self.generate_expression(&e.this)?;
23100                self.write(", ");
23101                self.generate_expression(&e.expression)?;
23102                self.write(")");
23103            }
23104        }
23105        Ok(())
23106    }
23107
23108    fn generate_execute_as_property(&mut self, e: &ExecuteAsProperty) -> Result<()> {
23109        // EXECUTE AS CALLER|OWNER|user
23110        self.write_keyword("EXECUTE AS");
23111        self.write_space();
23112        self.generate_expression(&e.this)?;
23113        Ok(())
23114    }
23115
23116    fn generate_export(&mut self, e: &Export) -> Result<()> {
23117        // BigQuery: EXPORT DATA [WITH CONNECTION connection] OPTIONS (...) AS query
23118        self.write_keyword("EXPORT DATA");
23119        if let Some(connection) = &e.connection {
23120            self.write_space();
23121            self.write_keyword("WITH CONNECTION");
23122            self.write_space();
23123            self.generate_expression(connection)?;
23124        }
23125        if !e.options.is_empty() {
23126            self.write_space();
23127            self.generate_options_clause(&e.options)?;
23128        }
23129        self.write_space();
23130        self.write_keyword("AS");
23131        self.write_space();
23132        self.generate_expression(&e.this)?;
23133        Ok(())
23134    }
23135
23136    fn generate_external_property(&mut self, e: &ExternalProperty) -> Result<()> {
23137        // EXTERNAL [this]
23138        self.write_keyword("EXTERNAL");
23139        if let Some(this) = &e.this {
23140            self.write_space();
23141            self.generate_expression(this)?;
23142        }
23143        Ok(())
23144    }
23145
23146    fn generate_fallback_property(&mut self, e: &FallbackProperty) -> Result<()> {
23147        // Python: {no}FALLBACK{protection}
23148        if e.no.is_some() {
23149            self.write_keyword("NO ");
23150        }
23151        self.write_keyword("FALLBACK");
23152        if e.protection.is_some() {
23153            self.write_keyword(" PROTECTION");
23154        }
23155        Ok(())
23156    }
23157
23158    fn generate_farm_fingerprint(&mut self, e: &FarmFingerprint) -> Result<()> {
23159        // BigQuery: FARM_FINGERPRINT(value)
23160        self.write_keyword("FARM_FINGERPRINT");
23161        self.write("(");
23162        for (i, expr) in e.expressions.iter().enumerate() {
23163            if i > 0 {
23164                self.write(", ");
23165            }
23166            self.generate_expression(expr)?;
23167        }
23168        self.write(")");
23169        Ok(())
23170    }
23171
23172    fn generate_features_at_time(&mut self, e: &FeaturesAtTime) -> Result<()> {
23173        // BigQuery ML: FEATURES_AT_TIME(feature_view, time, [num_rows], [ignore_feature_nulls])
23174        self.write_keyword("FEATURES_AT_TIME");
23175        self.write("(");
23176        self.generate_expression(&e.this)?;
23177        if let Some(time) = &e.time {
23178            self.write(", ");
23179            self.generate_expression(time)?;
23180        }
23181        if let Some(num_rows) = &e.num_rows {
23182            self.write(", ");
23183            self.generate_expression(num_rows)?;
23184        }
23185        if let Some(ignore_nulls) = &e.ignore_feature_nulls {
23186            self.write(", ");
23187            self.generate_expression(ignore_nulls)?;
23188        }
23189        self.write(")");
23190        Ok(())
23191    }
23192
23193    fn generate_fetch(&mut self, e: &Fetch) -> Result<()> {
23194        // For dialects that prefer LIMIT, convert simple FETCH to LIMIT
23195        let use_limit = !e.percent && !e.with_ties && e.count.is_some() && matches!(
23196            self.config.dialect,
23197            Some(DialectType::Spark) | Some(DialectType::Hive)
23198            | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
23199            | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
23200            | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
23201        );
23202
23203        if use_limit {
23204            self.write_keyword("LIMIT");
23205            self.write_space();
23206            self.generate_expression(e.count.as_ref().unwrap())?;
23207            return Ok(());
23208        }
23209
23210        // Python: FETCH direction count limit_options
23211        self.write_keyword("FETCH");
23212        if !e.direction.is_empty() {
23213            self.write_space();
23214            self.write_keyword(&e.direction);
23215        }
23216        if let Some(count) = &e.count {
23217            self.write_space();
23218            self.generate_expression(count)?;
23219        }
23220        // Generate PERCENT, ROWS, WITH TIES/ONLY
23221        if e.percent {
23222            self.write_keyword(" PERCENT");
23223        }
23224        if e.rows {
23225            self.write_keyword(" ROWS");
23226        }
23227        if e.with_ties {
23228            self.write_keyword(" WITH TIES");
23229        } else if e.rows {
23230            self.write_keyword(" ONLY");
23231        } else {
23232            self.write_keyword(" ROWS ONLY");
23233        }
23234        Ok(())
23235    }
23236
23237    fn generate_file_format_property(&mut self, e: &FileFormatProperty) -> Result<()> {
23238        // For Hive format: STORED AS this or STORED AS INPUTFORMAT x OUTPUTFORMAT y
23239        // For Spark/Databricks without hive_format: USING this
23240        // For Snowflake/others: FILE_FORMAT = this or FILE_FORMAT = (expressions)
23241        if e.hive_format.is_some() {
23242            // Hive format: STORED AS ...
23243            self.write_keyword("STORED AS");
23244            self.write_space();
23245            if let Some(this) = &e.this {
23246                // Uppercase the format name (e.g., parquet -> PARQUET)
23247                if let Expression::Identifier(id) = this.as_ref() {
23248                    self.write_keyword(&id.name.to_uppercase());
23249                } else {
23250                    self.generate_expression(this)?;
23251                }
23252            }
23253        } else if matches!(self.config.dialect, Some(DialectType::Hive)) {
23254            // Hive: STORED AS format
23255            self.write_keyword("STORED AS");
23256            self.write_space();
23257            if let Some(this) = &e.this {
23258                if let Expression::Identifier(id) = this.as_ref() {
23259                    self.write_keyword(&id.name.to_uppercase());
23260                } else {
23261                    self.generate_expression(this)?;
23262                }
23263            }
23264        } else if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks)) {
23265            // Spark/Databricks: USING format (e.g., USING DELTA)
23266            self.write_keyword("USING");
23267            self.write_space();
23268            if let Some(this) = &e.this {
23269                self.generate_expression(this)?;
23270            }
23271        } else {
23272            // Snowflake/standard format
23273            self.write_keyword("FILE_FORMAT");
23274            self.write(" = ");
23275            if let Some(this) = &e.this {
23276                self.generate_expression(this)?;
23277            } else if !e.expressions.is_empty() {
23278                self.write("(");
23279                for (i, expr) in e.expressions.iter().enumerate() {
23280                    if i > 0 {
23281                        self.write(", ");
23282                    }
23283                    self.generate_expression(expr)?;
23284                }
23285                self.write(")");
23286            }
23287        }
23288        Ok(())
23289    }
23290
23291    fn generate_filter(&mut self, e: &Filter) -> Result<()> {
23292        // agg_func FILTER(WHERE condition)
23293        self.generate_expression(&e.this)?;
23294        self.write_space();
23295        self.write_keyword("FILTER");
23296        self.write("(");
23297        self.write_keyword("WHERE");
23298        self.write_space();
23299        self.generate_expression(&e.expression)?;
23300        self.write(")");
23301        Ok(())
23302    }
23303
23304    fn generate_float64(&mut self, e: &Float64) -> Result<()> {
23305        // FLOAT64(this) or FLOAT64(this, expression)
23306        self.write_keyword("FLOAT64");
23307        self.write("(");
23308        self.generate_expression(&e.this)?;
23309        if let Some(expr) = &e.expression {
23310            self.write(", ");
23311            self.generate_expression(expr)?;
23312        }
23313        self.write(")");
23314        Ok(())
23315    }
23316
23317    fn generate_for_in(&mut self, e: &ForIn) -> Result<()> {
23318        // FOR this DO expression
23319        self.write_keyword("FOR");
23320        self.write_space();
23321        self.generate_expression(&e.this)?;
23322        self.write_space();
23323        self.write_keyword("DO");
23324        self.write_space();
23325        self.generate_expression(&e.expression)?;
23326        Ok(())
23327    }
23328
23329    fn generate_foreign_key(&mut self, e: &ForeignKey) -> Result<()> {
23330        // FOREIGN KEY (cols) REFERENCES table(cols) ON DELETE action ON UPDATE action
23331        self.write_keyword("FOREIGN KEY");
23332        if !e.expressions.is_empty() {
23333            self.write(" (");
23334            for (i, expr) in e.expressions.iter().enumerate() {
23335                if i > 0 {
23336                    self.write(", ");
23337                }
23338                self.generate_expression(expr)?;
23339            }
23340            self.write(")");
23341        }
23342        if let Some(reference) = &e.reference {
23343            self.write_space();
23344            self.generate_expression(reference)?;
23345        }
23346        if let Some(delete) = &e.delete {
23347            self.write_space();
23348            self.write_keyword("ON DELETE");
23349            self.write_space();
23350            self.generate_expression(delete)?;
23351        }
23352        if let Some(update) = &e.update {
23353            self.write_space();
23354            self.write_keyword("ON UPDATE");
23355            self.write_space();
23356            self.generate_expression(update)?;
23357        }
23358        if !e.options.is_empty() {
23359            self.write_space();
23360            for (i, opt) in e.options.iter().enumerate() {
23361                if i > 0 {
23362                    self.write_space();
23363                }
23364                self.generate_expression(opt)?;
23365            }
23366        }
23367        Ok(())
23368    }
23369
23370    fn generate_format(&mut self, e: &Format) -> Result<()> {
23371        // FORMAT(this, expressions...)
23372        self.write_keyword("FORMAT");
23373        self.write("(");
23374        self.generate_expression(&e.this)?;
23375        for expr in &e.expressions {
23376            self.write(", ");
23377            self.generate_expression(expr)?;
23378        }
23379        self.write(")");
23380        Ok(())
23381    }
23382
23383    fn generate_format_phrase(&mut self, e: &FormatPhrase) -> Result<()> {
23384        // Teradata: column (FORMAT 'format_string')
23385        self.generate_expression(&e.this)?;
23386        self.write(" (");
23387        self.write_keyword("FORMAT");
23388        self.write(" '");
23389        self.write(&e.format);
23390        self.write("')");
23391        Ok(())
23392    }
23393
23394    fn generate_freespace_property(&mut self, e: &FreespaceProperty) -> Result<()> {
23395        // Python: FREESPACE=this[PERCENT]
23396        self.write_keyword("FREESPACE");
23397        self.write("=");
23398        self.generate_expression(&e.this)?;
23399        if e.percent.is_some() {
23400            self.write_keyword(" PERCENT");
23401        }
23402        Ok(())
23403    }
23404
23405    fn generate_from(&mut self, e: &From) -> Result<()> {
23406        // Python: return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
23407        self.write_keyword("FROM");
23408        self.write_space();
23409
23410        // BigQuery, Hive, Spark, Databricks, SQLite, and ClickHouse prefer explicit CROSS JOIN over comma syntax
23411        // But keep commas when TABLESAMPLE is present
23412        use crate::dialects::DialectType;
23413        let has_tablesample = e.expressions.iter().any(|expr| matches!(expr, Expression::TableSample(_)));
23414        let use_cross_join = !has_tablesample && matches!(
23415            self.config.dialect,
23416            Some(DialectType::BigQuery)
23417                | Some(DialectType::Hive)
23418                | Some(DialectType::Spark)
23419                | Some(DialectType::Databricks)
23420                | Some(DialectType::SQLite)
23421                | Some(DialectType::ClickHouse)
23422        );
23423
23424        // Snowflake wraps standalone VALUES in FROM clause with parentheses
23425        let wrap_values_in_parens = matches!(
23426            self.config.dialect,
23427            Some(DialectType::Snowflake)
23428        );
23429
23430        for (i, expr) in e.expressions.iter().enumerate() {
23431            if i > 0 {
23432                if use_cross_join {
23433                    self.write(" CROSS JOIN ");
23434                } else {
23435                    self.write(", ");
23436                }
23437            }
23438            if wrap_values_in_parens && matches!(expr, Expression::Values(_)) {
23439                self.write("(");
23440                self.generate_expression(expr)?;
23441                self.write(")");
23442            } else {
23443                self.generate_expression(expr)?;
23444            }
23445        }
23446        Ok(())
23447    }
23448
23449    fn generate_from_base(&mut self, e: &FromBase) -> Result<()> {
23450        // FROM_BASE(this, expression) - convert from base N
23451        self.write_keyword("FROM_BASE");
23452        self.write("(");
23453        self.generate_expression(&e.this)?;
23454        self.write(", ");
23455        self.generate_expression(&e.expression)?;
23456        self.write(")");
23457        Ok(())
23458    }
23459
23460    fn generate_from_time_zone(&mut self, e: &FromTimeZone) -> Result<()> {
23461        // this AT TIME ZONE zone AT TIME ZONE 'UTC'
23462        self.generate_expression(&e.this)?;
23463        if let Some(zone) = &e.zone {
23464            self.write_space();
23465            self.write_keyword("AT TIME ZONE");
23466            self.write_space();
23467            self.generate_expression(zone)?;
23468            self.write_space();
23469            self.write_keyword("AT TIME ZONE");
23470            self.write(" 'UTC'");
23471        }
23472        Ok(())
23473    }
23474
23475    fn generate_gap_fill(&mut self, e: &GapFill) -> Result<()> {
23476        // GAP_FILL(this, ts_column, bucket_width, ...)
23477        self.write_keyword("GAP_FILL");
23478        self.write("(");
23479        self.generate_expression(&e.this)?;
23480        if let Some(ts_column) = &e.ts_column {
23481            self.write(", ");
23482            self.generate_expression(ts_column)?;
23483        }
23484        if let Some(bucket_width) = &e.bucket_width {
23485            self.write(", ");
23486            self.generate_expression(bucket_width)?;
23487        }
23488        if let Some(partitioning_columns) = &e.partitioning_columns {
23489            self.write(", ");
23490            self.generate_expression(partitioning_columns)?;
23491        }
23492        if let Some(value_columns) = &e.value_columns {
23493            self.write(", ");
23494            self.generate_expression(value_columns)?;
23495        }
23496        self.write(")");
23497        Ok(())
23498    }
23499
23500    fn generate_generate_date_array(&mut self, e: &GenerateDateArray) -> Result<()> {
23501        // GENERATE_DATE_ARRAY(start, end, step)
23502        self.write_keyword("GENERATE_DATE_ARRAY");
23503        self.write("(");
23504        let mut first = true;
23505        if let Some(start) = &e.start {
23506            self.generate_expression(start)?;
23507            first = false;
23508        }
23509        if let Some(end) = &e.end {
23510            if !first {
23511                self.write(", ");
23512            }
23513            self.generate_expression(end)?;
23514            first = false;
23515        }
23516        if let Some(step) = &e.step {
23517            if !first {
23518                self.write(", ");
23519            }
23520            self.generate_expression(step)?;
23521        }
23522        self.write(")");
23523        Ok(())
23524    }
23525
23526    fn generate_generate_embedding(&mut self, e: &GenerateEmbedding) -> Result<()> {
23527        // ML.GENERATE_EMBEDDING(model, content, params)
23528        self.write_keyword("ML.GENERATE_EMBEDDING");
23529        self.write("(");
23530        self.generate_expression(&e.this)?;
23531        self.write(", ");
23532        self.generate_expression(&e.expression)?;
23533        if let Some(params) = &e.params_struct {
23534            self.write(", ");
23535            self.generate_expression(params)?;
23536        }
23537        self.write(")");
23538        Ok(())
23539    }
23540
23541    fn generate_generate_series(&mut self, e: &GenerateSeries) -> Result<()> {
23542        // GENERATE_SERIES(start, end, step)
23543        self.write_keyword("GENERATE_SERIES");
23544        self.write("(");
23545        let mut first = true;
23546        if let Some(start) = &e.start {
23547            self.generate_expression(start)?;
23548            first = false;
23549        }
23550        if let Some(end) = &e.end {
23551            if !first {
23552                self.write(", ");
23553            }
23554            self.generate_expression(end)?;
23555            first = false;
23556        }
23557        if let Some(step) = &e.step {
23558            if !first {
23559                self.write(", ");
23560            }
23561            self.generate_expression(step)?;
23562        }
23563        self.write(")");
23564        Ok(())
23565    }
23566
23567    fn generate_generate_timestamp_array(&mut self, e: &GenerateTimestampArray) -> Result<()> {
23568        // GENERATE_TIMESTAMP_ARRAY(start, end, step)
23569        self.write_keyword("GENERATE_TIMESTAMP_ARRAY");
23570        self.write("(");
23571        let mut first = true;
23572        if let Some(start) = &e.start {
23573            self.generate_expression(start)?;
23574            first = false;
23575        }
23576        if let Some(end) = &e.end {
23577            if !first {
23578                self.write(", ");
23579            }
23580            self.generate_expression(end)?;
23581            first = false;
23582        }
23583        if let Some(step) = &e.step {
23584            if !first {
23585                self.write(", ");
23586            }
23587            self.generate_expression(step)?;
23588        }
23589        self.write(")");
23590        Ok(())
23591    }
23592
23593    fn generate_generated_as_identity_column_constraint(&mut self, e: &GeneratedAsIdentityColumnConstraint) -> Result<()> {
23594        use crate::dialects::DialectType;
23595
23596        // For Snowflake, use AUTOINCREMENT START x INCREMENT y syntax
23597        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
23598            self.write_keyword("AUTOINCREMENT");
23599            if let Some(start) = &e.start {
23600                self.write_keyword(" START ");
23601                self.generate_expression(start)?;
23602            }
23603            if let Some(increment) = &e.increment {
23604                self.write_keyword(" INCREMENT ");
23605                self.generate_expression(increment)?;
23606            }
23607            return Ok(());
23608        }
23609
23610        // Python: GENERATED [ALWAYS|BY DEFAULT [ON NULL]] AS IDENTITY [(start, increment, ...)]
23611        self.write_keyword("GENERATED");
23612        if let Some(this) = &e.this {
23613            // Check if it's a truthy boolean expression
23614            if let Expression::Boolean(b) = this.as_ref() {
23615                if b.value {
23616                    self.write_keyword(" ALWAYS");
23617                } else {
23618                    self.write_keyword(" BY DEFAULT");
23619                    if e.on_null.is_some() {
23620                        self.write_keyword(" ON NULL");
23621                    }
23622                }
23623            } else {
23624                self.write_keyword(" ALWAYS");
23625            }
23626        }
23627        self.write_keyword(" AS IDENTITY");
23628        // Add sequence options if any
23629        let has_options = e.start.is_some() || e.increment.is_some() || e.minvalue.is_some() || e.maxvalue.is_some();
23630        if has_options {
23631            self.write(" (");
23632            let mut first = true;
23633            if let Some(start) = &e.start {
23634                self.write_keyword("START WITH ");
23635                self.generate_expression(start)?;
23636                first = false;
23637            }
23638            if let Some(increment) = &e.increment {
23639                if !first { self.write(" "); }
23640                self.write_keyword("INCREMENT BY ");
23641                self.generate_expression(increment)?;
23642                first = false;
23643            }
23644            if let Some(minvalue) = &e.minvalue {
23645                if !first { self.write(" "); }
23646                self.write_keyword("MINVALUE ");
23647                self.generate_expression(minvalue)?;
23648                first = false;
23649            }
23650            if let Some(maxvalue) = &e.maxvalue {
23651                if !first { self.write(" "); }
23652                self.write_keyword("MAXVALUE ");
23653                self.generate_expression(maxvalue)?;
23654            }
23655            self.write(")");
23656        }
23657        Ok(())
23658    }
23659
23660    fn generate_generated_as_row_column_constraint(&mut self, e: &GeneratedAsRowColumnConstraint) -> Result<()> {
23661        // Python: GENERATED ALWAYS AS ROW START|END [HIDDEN]
23662        self.write_keyword("GENERATED ALWAYS AS ROW ");
23663        if e.start.is_some() {
23664            self.write_keyword("START");
23665        } else {
23666            self.write_keyword("END");
23667        }
23668        if e.hidden.is_some() {
23669            self.write_keyword(" HIDDEN");
23670        }
23671        Ok(())
23672    }
23673
23674    fn generate_get(&mut self, e: &Get) -> Result<()> {
23675        // GET this target properties
23676        self.write_keyword("GET");
23677        self.write_space();
23678        self.generate_expression(&e.this)?;
23679        if let Some(target) = &e.target {
23680            self.write_space();
23681            self.generate_expression(target)?;
23682        }
23683        for prop in &e.properties {
23684            self.write_space();
23685            self.generate_expression(prop)?;
23686        }
23687        Ok(())
23688    }
23689
23690    fn generate_get_extract(&mut self, e: &GetExtract) -> Result<()> {
23691        // GetExtract generates bracket access: this[expression]
23692        self.generate_expression(&e.this)?;
23693        self.write("[");
23694        self.generate_expression(&e.expression)?;
23695        self.write("]");
23696        Ok(())
23697    }
23698
23699    fn generate_getbit(&mut self, e: &Getbit) -> Result<()> {
23700        // GETBIT(this, expression) or GET_BIT(this, expression)
23701        self.write_keyword("GETBIT");
23702        self.write("(");
23703        self.generate_expression(&e.this)?;
23704        self.write(", ");
23705        self.generate_expression(&e.expression)?;
23706        self.write(")");
23707        Ok(())
23708    }
23709
23710    fn generate_grant_principal(&mut self, e: &GrantPrincipal) -> Result<()> {
23711        // [ROLE|GROUP] name (e.g., "ROLE admin", "GROUP qa_users", or just "user1")
23712        if e.is_role {
23713            self.write_keyword("ROLE");
23714            self.write_space();
23715        } else if e.is_group {
23716            self.write_keyword("GROUP");
23717            self.write_space();
23718        }
23719        self.write(&e.name.name);
23720        Ok(())
23721    }
23722
23723    fn generate_grant_privilege(&mut self, e: &GrantPrivilege) -> Result<()> {
23724        // privilege(columns) or just privilege
23725        self.generate_expression(&e.this)?;
23726        if !e.expressions.is_empty() {
23727            self.write("(");
23728            for (i, expr) in e.expressions.iter().enumerate() {
23729                if i > 0 {
23730                    self.write(", ");
23731                }
23732                self.generate_expression(expr)?;
23733            }
23734            self.write(")");
23735        }
23736        Ok(())
23737    }
23738
23739    fn generate_group(&mut self, e: &Group) -> Result<()> {
23740        // Python handles GROUP BY ALL/DISTINCT modifiers and grouping expressions
23741        self.write_keyword("GROUP BY");
23742        // Handle ALL/DISTINCT modifier: Some(true) = ALL, Some(false) = DISTINCT
23743        match e.all {
23744            Some(true) => {
23745                self.write_space();
23746                self.write_keyword("ALL");
23747            }
23748            Some(false) => {
23749                self.write_space();
23750                self.write_keyword("DISTINCT");
23751            }
23752            None => {}
23753        }
23754        if !e.expressions.is_empty() {
23755            self.write_space();
23756            for (i, expr) in e.expressions.iter().enumerate() {
23757                if i > 0 {
23758                    self.write(", ");
23759                }
23760                self.generate_expression(expr)?;
23761            }
23762        }
23763        // Handle CUBE, ROLLUP, GROUPING SETS
23764        if let Some(cube) = &e.cube {
23765            if !e.expressions.is_empty() {
23766                self.write(", ");
23767            } else {
23768                self.write_space();
23769            }
23770            self.generate_expression(cube)?;
23771        }
23772        if let Some(rollup) = &e.rollup {
23773            if !e.expressions.is_empty() || e.cube.is_some() {
23774                self.write(", ");
23775            } else {
23776                self.write_space();
23777            }
23778            self.generate_expression(rollup)?;
23779        }
23780        if let Some(grouping_sets) = &e.grouping_sets {
23781            if !e.expressions.is_empty() || e.cube.is_some() || e.rollup.is_some() {
23782                self.write(", ");
23783            } else {
23784                self.write_space();
23785            }
23786            self.generate_expression(grouping_sets)?;
23787        }
23788        if let Some(totals) = &e.totals {
23789            self.write_space();
23790            self.write_keyword("WITH TOTALS");
23791            self.generate_expression(totals)?;
23792        }
23793        Ok(())
23794    }
23795
23796    fn generate_group_by(&mut self, e: &GroupBy) -> Result<()> {
23797        // GROUP BY expressions
23798        self.write_keyword("GROUP BY");
23799        // Handle ALL/DISTINCT modifier: Some(true) = ALL, Some(false) = DISTINCT
23800        match e.all {
23801            Some(true) => {
23802                self.write_space();
23803                self.write_keyword("ALL");
23804            }
23805            Some(false) => {
23806                self.write_space();
23807                self.write_keyword("DISTINCT");
23808            }
23809            None => {}
23810        }
23811
23812        // Check for trailing WITH CUBE or WITH ROLLUP (Hive/MySQL syntax)
23813        // These are represented as Cube/Rollup expressions with empty expressions at the end
23814        let mut trailing_cube = false;
23815        let mut trailing_rollup = false;
23816        let mut regular_expressions: Vec<&Expression> = Vec::new();
23817
23818        for expr in &e.expressions {
23819            match expr {
23820                Expression::Cube(c) if c.expressions.is_empty() => {
23821                    trailing_cube = true;
23822                }
23823                Expression::Rollup(r) if r.expressions.is_empty() => {
23824                    trailing_rollup = true;
23825                }
23826                _ => {
23827                    regular_expressions.push(expr);
23828                }
23829            }
23830        }
23831
23832        // In pretty mode, put columns on separate lines
23833        if self.config.pretty {
23834            self.write_newline();
23835            self.indent_level += 1;
23836            for (i, expr) in regular_expressions.iter().enumerate() {
23837                if i > 0 {
23838                    self.write(",");
23839                    self.write_newline();
23840                }
23841                self.write_indent();
23842                self.generate_expression(expr)?;
23843            }
23844            self.indent_level -= 1;
23845        } else {
23846            self.write_space();
23847            for (i, expr) in regular_expressions.iter().enumerate() {
23848                if i > 0 {
23849                    self.write(", ");
23850                }
23851                self.generate_expression(expr)?;
23852            }
23853        }
23854
23855        // Output trailing WITH CUBE or WITH ROLLUP
23856        if trailing_cube {
23857            self.write_space();
23858            self.write_keyword("WITH CUBE");
23859        } else if trailing_rollup {
23860            self.write_space();
23861            self.write_keyword("WITH ROLLUP");
23862        }
23863
23864        // ClickHouse: WITH TOTALS
23865        if e.totals {
23866            self.write_space();
23867            self.write_keyword("WITH TOTALS");
23868        }
23869
23870        Ok(())
23871    }
23872
23873    fn generate_grouping(&mut self, e: &Grouping) -> Result<()> {
23874        // GROUPING(col1, col2, ...)
23875        self.write_keyword("GROUPING");
23876        self.write("(");
23877        for (i, expr) in e.expressions.iter().enumerate() {
23878            if i > 0 {
23879                self.write(", ");
23880            }
23881            self.generate_expression(expr)?;
23882        }
23883        self.write(")");
23884        Ok(())
23885    }
23886
23887    fn generate_grouping_id(&mut self, e: &GroupingId) -> Result<()> {
23888        // GROUPING_ID(col1, col2, ...)
23889        self.write_keyword("GROUPING_ID");
23890        self.write("(");
23891        for (i, expr) in e.expressions.iter().enumerate() {
23892            if i > 0 {
23893                self.write(", ");
23894            }
23895            self.generate_expression(expr)?;
23896        }
23897        self.write(")");
23898        Ok(())
23899    }
23900
23901    fn generate_grouping_sets(&mut self, e: &GroupingSets) -> Result<()> {
23902        // Python: return f"GROUPING SETS {self.wrap(grouping_sets)}"
23903        self.write_keyword("GROUPING SETS");
23904        self.write(" (");
23905        for (i, expr) in e.expressions.iter().enumerate() {
23906            if i > 0 {
23907                self.write(", ");
23908            }
23909            self.generate_expression(expr)?;
23910        }
23911        self.write(")");
23912        Ok(())
23913    }
23914
23915    fn generate_hash_agg(&mut self, e: &HashAgg) -> Result<()> {
23916        // HASH_AGG(this, expressions...)
23917        self.write_keyword("HASH_AGG");
23918        self.write("(");
23919        self.generate_expression(&e.this)?;
23920        for expr in &e.expressions {
23921            self.write(", ");
23922            self.generate_expression(expr)?;
23923        }
23924        self.write(")");
23925        Ok(())
23926    }
23927
23928    fn generate_having(&mut self, e: &Having) -> Result<()> {
23929        // Python: return f"{self.seg('HAVING')}{self.sep()}{this}"
23930        self.write_keyword("HAVING");
23931        self.write_space();
23932        self.generate_expression(&e.this)?;
23933        Ok(())
23934    }
23935
23936    fn generate_having_max(&mut self, e: &HavingMax) -> Result<()> {
23937        // Python: this HAVING MAX|MIN expression
23938        self.generate_expression(&e.this)?;
23939        self.write_space();
23940        self.write_keyword("HAVING");
23941        self.write_space();
23942        if e.max.is_some() {
23943            self.write_keyword("MAX");
23944        } else {
23945            self.write_keyword("MIN");
23946        }
23947        self.write_space();
23948        self.generate_expression(&e.expression)?;
23949        Ok(())
23950    }
23951
23952    fn generate_heredoc(&mut self, e: &Heredoc) -> Result<()> {
23953        use crate::dialects::DialectType;
23954        // DuckDB: convert dollar-tagged strings to single-quoted
23955        if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
23956            // Extract the string content and output as single-quoted
23957            if let Expression::Literal(Literal::String(ref s)) = *e.this {
23958                return self.generate_string_literal(s);
23959            }
23960        }
23961        // PostgreSQL: preserve dollar-quoting
23962        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
23963            self.write("$");
23964            if let Some(tag) = &e.tag {
23965                self.generate_expression(tag)?;
23966            }
23967            self.write("$");
23968            self.generate_expression(&e.this)?;
23969            self.write("$");
23970            if let Some(tag) = &e.tag {
23971                self.generate_expression(tag)?;
23972            }
23973            self.write("$");
23974            return Ok(());
23975        }
23976        // Default: output as dollar-tagged
23977        self.write("$");
23978        if let Some(tag) = &e.tag {
23979            self.generate_expression(tag)?;
23980        }
23981        self.write("$");
23982        self.generate_expression(&e.this)?;
23983        self.write("$");
23984        if let Some(tag) = &e.tag {
23985            self.generate_expression(tag)?;
23986        }
23987        self.write("$");
23988        Ok(())
23989    }
23990
23991    fn generate_hex_encode(&mut self, e: &HexEncode) -> Result<()> {
23992        // HEX_ENCODE(this)
23993        self.write_keyword("HEX_ENCODE");
23994        self.write("(");
23995        self.generate_expression(&e.this)?;
23996        self.write(")");
23997        Ok(())
23998    }
23999
24000    fn generate_historical_data(&mut self, e: &HistoricalData) -> Result<()> {
24001        // Python: this (kind => expression)
24002        // Write the keyword (AT/BEFORE/END) directly to avoid quoting it as a reserved word
24003        match e.this.as_ref() {
24004            Expression::Identifier(id) => self.write(&id.name),
24005            other => self.generate_expression(other)?,
24006        }
24007        self.write(" (");
24008        self.write(&e.kind);
24009        self.write(" => ");
24010        self.generate_expression(&e.expression)?;
24011        self.write(")");
24012        Ok(())
24013    }
24014
24015    fn generate_hll(&mut self, e: &Hll) -> Result<()> {
24016        // HLL(this, expressions...)
24017        self.write_keyword("HLL");
24018        self.write("(");
24019        self.generate_expression(&e.this)?;
24020        for expr in &e.expressions {
24021            self.write(", ");
24022            self.generate_expression(expr)?;
24023        }
24024        self.write(")");
24025        Ok(())
24026    }
24027
24028    fn generate_in_out_column_constraint(&mut self, e: &InOutColumnConstraint) -> Result<()> {
24029        // Python: IN|OUT|IN OUT
24030        if e.input_.is_some() && e.output.is_some() {
24031            self.write_keyword("IN OUT");
24032        } else if e.input_.is_some() {
24033            self.write_keyword("IN");
24034        } else if e.output.is_some() {
24035            self.write_keyword("OUT");
24036        }
24037        Ok(())
24038    }
24039
24040    fn generate_include_property(&mut self, e: &IncludeProperty) -> Result<()> {
24041        // Python: INCLUDE this [column_def] [AS alias]
24042        self.write_keyword("INCLUDE");
24043        self.write_space();
24044        self.generate_expression(&e.this)?;
24045        if let Some(column_def) = &e.column_def {
24046            self.write_space();
24047            self.generate_expression(column_def)?;
24048        }
24049        if let Some(alias) = &e.alias {
24050            self.write_space();
24051            self.write_keyword("AS");
24052            self.write_space();
24053            self.write(alias);
24054        }
24055        Ok(())
24056    }
24057
24058    fn generate_index(&mut self, e: &Index) -> Result<()> {
24059        // [UNIQUE] [PRIMARY] [AMP] INDEX [name] [ON table] (params)
24060        if e.unique {
24061            self.write_keyword("UNIQUE");
24062            self.write_space();
24063        }
24064        if e.primary.is_some() {
24065            self.write_keyword("PRIMARY");
24066            self.write_space();
24067        }
24068        if e.amp.is_some() {
24069            self.write_keyword("AMP");
24070            self.write_space();
24071        }
24072        if e.table.is_none() {
24073            self.write_keyword("INDEX");
24074            self.write_space();
24075        }
24076        if let Some(name) = &e.this {
24077            self.generate_expression(name)?;
24078            self.write_space();
24079        }
24080        if let Some(table) = &e.table {
24081            self.write_keyword("ON");
24082            self.write_space();
24083            self.generate_expression(table)?;
24084        }
24085        if !e.params.is_empty() {
24086            self.write("(");
24087            for (i, param) in e.params.iter().enumerate() {
24088                if i > 0 {
24089                    self.write(", ");
24090                }
24091                self.generate_expression(param)?;
24092            }
24093            self.write(")");
24094        }
24095        Ok(())
24096    }
24097
24098    fn generate_index_column_constraint(&mut self, e: &IndexColumnConstraint) -> Result<()> {
24099        // Python: kind INDEX [this] [USING index_type] (expressions) [options]
24100        if let Some(kind) = &e.kind {
24101            self.write(kind);
24102            self.write_space();
24103        }
24104        self.write_keyword("INDEX");
24105        if let Some(this) = &e.this {
24106            self.write_space();
24107            self.generate_expression(this)?;
24108        }
24109        if let Some(index_type) = &e.index_type {
24110            self.write_space();
24111            self.write_keyword("USING");
24112            self.write_space();
24113            self.generate_expression(index_type)?;
24114        }
24115        if !e.expressions.is_empty() {
24116            self.write(" (");
24117            for (i, expr) in e.expressions.iter().enumerate() {
24118                if i > 0 {
24119                    self.write(", ");
24120                }
24121                self.generate_expression(expr)?;
24122            }
24123            self.write(")");
24124        }
24125        for opt in &e.options {
24126            self.write_space();
24127            self.generate_expression(opt)?;
24128        }
24129        Ok(())
24130    }
24131
24132    fn generate_index_constraint_option(&mut self, e: &IndexConstraintOption) -> Result<()> {
24133        // Python: KEY_BLOCK_SIZE = x | USING x | WITH PARSER x | COMMENT x | visible | engine_attr | secondary_engine_attr
24134        if let Some(key_block_size) = &e.key_block_size {
24135            self.write_keyword("KEY_BLOCK_SIZE");
24136            self.write(" = ");
24137            self.generate_expression(key_block_size)?;
24138        } else if let Some(using) = &e.using {
24139            self.write_keyword("USING");
24140            self.write_space();
24141            self.generate_expression(using)?;
24142        } else if let Some(parser) = &e.parser {
24143            self.write_keyword("WITH PARSER");
24144            self.write_space();
24145            self.generate_expression(parser)?;
24146        } else if let Some(comment) = &e.comment {
24147            self.write_keyword("COMMENT");
24148            self.write_space();
24149            self.generate_expression(comment)?;
24150        } else if let Some(visible) = &e.visible {
24151            self.generate_expression(visible)?;
24152        } else if let Some(engine_attr) = &e.engine_attr {
24153            self.write_keyword("ENGINE_ATTRIBUTE");
24154            self.write(" = ");
24155            self.generate_expression(engine_attr)?;
24156        } else if let Some(secondary_engine_attr) = &e.secondary_engine_attr {
24157            self.write_keyword("SECONDARY_ENGINE_ATTRIBUTE");
24158            self.write(" = ");
24159            self.generate_expression(secondary_engine_attr)?;
24160        }
24161        Ok(())
24162    }
24163
24164    fn generate_index_parameters(&mut self, e: &IndexParameters) -> Result<()> {
24165        // Python: [USING using] (columns) [PARTITION BY partition_by] [where] [INCLUDE (include)] [WITH (with_storage)] [USING INDEX TABLESPACE tablespace]
24166        if let Some(using) = &e.using {
24167            self.write_keyword("USING");
24168            self.write_space();
24169            self.generate_expression(using)?;
24170        }
24171        if !e.columns.is_empty() {
24172            self.write("(");
24173            for (i, col) in e.columns.iter().enumerate() {
24174                if i > 0 {
24175                    self.write(", ");
24176                }
24177                self.generate_expression(col)?;
24178            }
24179            self.write(")");
24180        }
24181        if let Some(partition_by) = &e.partition_by {
24182            self.write_space();
24183            self.write_keyword("PARTITION BY");
24184            self.write_space();
24185            self.generate_expression(partition_by)?;
24186        }
24187        if let Some(where_) = &e.where_ {
24188            self.write_space();
24189            self.generate_expression(where_)?;
24190        }
24191        if let Some(include) = &e.include {
24192            self.write_space();
24193            self.write_keyword("INCLUDE");
24194            self.write(" (");
24195            self.generate_expression(include)?;
24196            self.write(")");
24197        }
24198        if let Some(with_storage) = &e.with_storage {
24199            self.write_space();
24200            self.write_keyword("WITH");
24201            self.write(" (");
24202            self.generate_expression(with_storage)?;
24203            self.write(")");
24204        }
24205        if let Some(tablespace) = &e.tablespace {
24206            self.write_space();
24207            self.write_keyword("USING INDEX TABLESPACE");
24208            self.write_space();
24209            self.generate_expression(tablespace)?;
24210        }
24211        Ok(())
24212    }
24213
24214    fn generate_index_table_hint(&mut self, e: &IndexTableHint) -> Result<()> {
24215        // Python: this INDEX [FOR target] (expressions)
24216        // Write hint type (USE/IGNORE/FORCE) as keyword, not through generate_expression
24217        // to avoid quoting reserved keywords like IGNORE, FORCE, JOIN
24218        if let Expression::Identifier(id) = &*e.this {
24219            self.write_keyword(&id.name);
24220        } else {
24221            self.generate_expression(&e.this)?;
24222        }
24223        self.write_space();
24224        self.write_keyword("INDEX");
24225        if let Some(target) = &e.target {
24226            self.write_space();
24227            self.write_keyword("FOR");
24228            self.write_space();
24229            if let Expression::Identifier(id) = &**target {
24230                self.write_keyword(&id.name);
24231            } else {
24232                self.generate_expression(target)?;
24233            }
24234        }
24235        // Always output parentheses (even if empty, e.g. USE INDEX ())
24236        self.write(" (");
24237        for (i, expr) in e.expressions.iter().enumerate() {
24238            if i > 0 {
24239                self.write(", ");
24240            }
24241            self.generate_expression(expr)?;
24242        }
24243        self.write(")");
24244        Ok(())
24245    }
24246
24247    fn generate_inherits_property(&mut self, e: &InheritsProperty) -> Result<()> {
24248        // INHERITS (table1, table2, ...)
24249        self.write_keyword("INHERITS");
24250        self.write(" (");
24251        for (i, expr) in e.expressions.iter().enumerate() {
24252            if i > 0 {
24253                self.write(", ");
24254            }
24255            self.generate_expression(expr)?;
24256        }
24257        self.write(")");
24258        Ok(())
24259    }
24260
24261    fn generate_input_model_property(&mut self, e: &InputModelProperty) -> Result<()> {
24262        // INPUT(model)
24263        self.write_keyword("INPUT");
24264        self.write("(");
24265        self.generate_expression(&e.this)?;
24266        self.write(")");
24267        Ok(())
24268    }
24269
24270    fn generate_input_output_format(&mut self, e: &InputOutputFormat) -> Result<()> {
24271        // Python: INPUTFORMAT input_format OUTPUTFORMAT output_format
24272        if let Some(input_format) = &e.input_format {
24273            self.write_keyword("INPUTFORMAT");
24274            self.write_space();
24275            self.generate_expression(input_format)?;
24276        }
24277        if let Some(output_format) = &e.output_format {
24278            if e.input_format.is_some() {
24279                self.write(" ");
24280            }
24281            self.write_keyword("OUTPUTFORMAT");
24282            self.write_space();
24283            self.generate_expression(output_format)?;
24284        }
24285        Ok(())
24286    }
24287
24288    fn generate_install(&mut self, e: &Install) -> Result<()> {
24289        // [FORCE] INSTALL extension [FROM source]
24290        if e.force.is_some() {
24291            self.write_keyword("FORCE");
24292            self.write_space();
24293        }
24294        self.write_keyword("INSTALL");
24295        self.write_space();
24296        self.generate_expression(&e.this)?;
24297        if let Some(from) = &e.from_ {
24298            self.write_space();
24299            self.write_keyword("FROM");
24300            self.write_space();
24301            self.generate_expression(from)?;
24302        }
24303        Ok(())
24304    }
24305
24306    fn generate_interval_op(&mut self, e: &IntervalOp) -> Result<()> {
24307        // INTERVAL 'expression' unit
24308        self.write_keyword("INTERVAL");
24309        self.write_space();
24310        // When a unit is specified and the expression is a number,
24311        self.generate_expression(&e.expression)?;
24312        if let Some(unit) = &e.unit {
24313            self.write_space();
24314            self.write(unit);
24315        }
24316        Ok(())
24317    }
24318
24319    fn generate_interval_span(&mut self, e: &IntervalSpan) -> Result<()> {
24320        // unit TO unit (e.g., HOUR TO SECOND)
24321        self.write(&format!("{:?}", e.this).to_uppercase());
24322        self.write_space();
24323        self.write_keyword("TO");
24324        self.write_space();
24325        self.write(&format!("{:?}", e.expression).to_uppercase());
24326        Ok(())
24327    }
24328
24329    fn generate_into_clause(&mut self, e: &IntoClause) -> Result<()> {
24330        // INTO [TEMPORARY|UNLOGGED] table
24331        self.write_keyword("INTO");
24332        if e.temporary {
24333            self.write_keyword(" TEMPORARY");
24334        }
24335        if e.unlogged.is_some() {
24336            self.write_keyword(" UNLOGGED");
24337        }
24338        if let Some(this) = &e.this {
24339            self.write_space();
24340            self.generate_expression(this)?;
24341        }
24342        if !e.expressions.is_empty() {
24343            self.write(" (");
24344            for (i, expr) in e.expressions.iter().enumerate() {
24345                if i > 0 {
24346                    self.write(", ");
24347                }
24348                self.generate_expression(expr)?;
24349            }
24350            self.write(")");
24351        }
24352        Ok(())
24353    }
24354
24355    fn generate_introducer(&mut self, e: &Introducer) -> Result<()> {
24356        // Python: this expression (e.g., _utf8 'string')
24357        self.generate_expression(&e.this)?;
24358        self.write_space();
24359        self.generate_expression(&e.expression)?;
24360        Ok(())
24361    }
24362
24363    fn generate_isolated_loading_property(&mut self, e: &IsolatedLoadingProperty) -> Result<()> {
24364        // Python: WITH [NO] [CONCURRENT] ISOLATED LOADING [target]
24365        self.write_keyword("WITH");
24366        if e.no.is_some() {
24367            self.write_keyword(" NO");
24368        }
24369        if e.concurrent.is_some() {
24370            self.write_keyword(" CONCURRENT");
24371        }
24372        self.write_keyword(" ISOLATED LOADING");
24373        if let Some(target) = &e.target {
24374            self.write_space();
24375            self.generate_expression(target)?;
24376        }
24377        Ok(())
24378    }
24379
24380    fn generate_json(&mut self, e: &JSON) -> Result<()> {
24381        // Python: JSON [this] [WITHOUT|WITH] [UNIQUE KEYS]
24382        self.write_keyword("JSON");
24383        if let Some(this) = &e.this {
24384            self.write_space();
24385            self.generate_expression(this)?;
24386        }
24387        if let Some(with_) = &e.with_ {
24388            // Check if it's a truthy boolean
24389            if let Expression::Boolean(b) = with_.as_ref() {
24390                if b.value {
24391                    self.write_keyword(" WITH");
24392                } else {
24393                    self.write_keyword(" WITHOUT");
24394                }
24395            }
24396        }
24397        if e.unique {
24398            self.write_keyword(" UNIQUE KEYS");
24399        }
24400        Ok(())
24401    }
24402
24403    fn generate_json_array(&mut self, e: &JSONArray) -> Result<()> {
24404        // Python: return self.func("JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})")
24405        self.write_keyword("JSON_ARRAY");
24406        self.write("(");
24407        for (i, expr) in e.expressions.iter().enumerate() {
24408            if i > 0 {
24409                self.write(", ");
24410            }
24411            self.generate_expression(expr)?;
24412        }
24413        if let Some(null_handling) = &e.null_handling {
24414            self.write_space();
24415            self.generate_expression(null_handling)?;
24416        }
24417        if let Some(return_type) = &e.return_type {
24418            self.write_space();
24419            self.write_keyword("RETURNING");
24420            self.write_space();
24421            self.generate_expression(return_type)?;
24422        }
24423        if e.strict.is_some() {
24424            self.write_space();
24425            self.write_keyword("STRICT");
24426        }
24427        self.write(")");
24428        Ok(())
24429    }
24430
24431    fn generate_json_array_agg_struct(&mut self, e: &JSONArrayAgg) -> Result<()> {
24432        // JSON_ARRAYAGG(this [ORDER BY ...] [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
24433        self.write_keyword("JSON_ARRAYAGG");
24434        self.write("(");
24435        self.generate_expression(&e.this)?;
24436        if let Some(order) = &e.order {
24437            self.write_space();
24438            // Order is stored as an OrderBy expression
24439            if let Expression::OrderBy(ob) = order.as_ref() {
24440                self.write_keyword("ORDER BY");
24441                self.write_space();
24442                for (i, ord) in ob.expressions.iter().enumerate() {
24443                    if i > 0 {
24444                        self.write(", ");
24445                    }
24446                    self.generate_ordered(ord)?;
24447                }
24448            } else {
24449                // Fallback: generate the expression directly
24450                self.generate_expression(order)?;
24451            }
24452        }
24453        if let Some(null_handling) = &e.null_handling {
24454            self.write_space();
24455            self.generate_expression(null_handling)?;
24456        }
24457        if let Some(return_type) = &e.return_type {
24458            self.write_space();
24459            self.write_keyword("RETURNING");
24460            self.write_space();
24461            self.generate_expression(return_type)?;
24462        }
24463        if e.strict.is_some() {
24464            self.write_space();
24465            self.write_keyword("STRICT");
24466        }
24467        self.write(")");
24468        Ok(())
24469    }
24470
24471    fn generate_json_object_agg_struct(&mut self, e: &JSONObjectAgg) -> Result<()> {
24472        // JSON_OBJECTAGG(key: value [NULL ON NULL | ABSENT ON NULL] [WITH UNIQUE KEYS] [RETURNING type])
24473        self.write_keyword("JSON_OBJECTAGG");
24474        self.write("(");
24475        for (i, expr) in e.expressions.iter().enumerate() {
24476            if i > 0 {
24477                self.write(", ");
24478            }
24479            self.generate_expression(expr)?;
24480        }
24481        if let Some(null_handling) = &e.null_handling {
24482            self.write_space();
24483            self.generate_expression(null_handling)?;
24484        }
24485        if let Some(unique_keys) = &e.unique_keys {
24486            self.write_space();
24487            if let Expression::Boolean(b) = unique_keys.as_ref() {
24488                if b.value {
24489                    self.write_keyword("WITH UNIQUE KEYS");
24490                } else {
24491                    self.write_keyword("WITHOUT UNIQUE KEYS");
24492                }
24493            }
24494        }
24495        if let Some(return_type) = &e.return_type {
24496            self.write_space();
24497            self.write_keyword("RETURNING");
24498            self.write_space();
24499            self.generate_expression(return_type)?;
24500        }
24501        self.write(")");
24502        Ok(())
24503    }
24504
24505    fn generate_json_array_append(&mut self, e: &JSONArrayAppend) -> Result<()> {
24506        // JSON_ARRAY_APPEND(this, path, value, ...)
24507        self.write_keyword("JSON_ARRAY_APPEND");
24508        self.write("(");
24509        self.generate_expression(&e.this)?;
24510        for expr in &e.expressions {
24511            self.write(", ");
24512            self.generate_expression(expr)?;
24513        }
24514        self.write(")");
24515        Ok(())
24516    }
24517
24518    fn generate_json_array_contains(&mut self, e: &JSONArrayContains) -> Result<()> {
24519        // JSON_ARRAY_CONTAINS(this, expression)
24520        self.write_keyword("JSON_ARRAY_CONTAINS");
24521        self.write("(");
24522        self.generate_expression(&e.this)?;
24523        self.write(", ");
24524        self.generate_expression(&e.expression)?;
24525        self.write(")");
24526        Ok(())
24527    }
24528
24529    fn generate_json_array_insert(&mut self, e: &JSONArrayInsert) -> Result<()> {
24530        // JSON_ARRAY_INSERT(this, path, value, ...)
24531        self.write_keyword("JSON_ARRAY_INSERT");
24532        self.write("(");
24533        self.generate_expression(&e.this)?;
24534        for expr in &e.expressions {
24535            self.write(", ");
24536            self.generate_expression(expr)?;
24537        }
24538        self.write(")");
24539        Ok(())
24540    }
24541
24542    fn generate_jsonb_exists(&mut self, e: &JSONBExists) -> Result<()> {
24543        // JSONB_EXISTS(this, path)
24544        self.write_keyword("JSONB_EXISTS");
24545        self.write("(");
24546        self.generate_expression(&e.this)?;
24547        if let Some(path) = &e.path {
24548            self.write(", ");
24549            self.generate_expression(path)?;
24550        }
24551        self.write(")");
24552        Ok(())
24553    }
24554
24555    fn generate_jsonb_extract_scalar(&mut self, e: &JSONBExtractScalar) -> Result<()> {
24556        // JSONB_EXTRACT_SCALAR(this, expression)
24557        self.write_keyword("JSONB_EXTRACT_SCALAR");
24558        self.write("(");
24559        self.generate_expression(&e.this)?;
24560        self.write(", ");
24561        self.generate_expression(&e.expression)?;
24562        self.write(")");
24563        Ok(())
24564    }
24565
24566    fn generate_jsonb_object_agg(&mut self, e: &JSONBObjectAgg) -> Result<()> {
24567        // JSONB_OBJECT_AGG(this, expression)
24568        self.write_keyword("JSONB_OBJECT_AGG");
24569        self.write("(");
24570        self.generate_expression(&e.this)?;
24571        self.write(", ");
24572        self.generate_expression(&e.expression)?;
24573        self.write(")");
24574        Ok(())
24575    }
24576
24577    fn generate_json_column_def(&mut self, e: &JSONColumnDef) -> Result<()> {
24578        // Python: NESTED PATH path schema | this kind PATH path [FOR ORDINALITY]
24579        if let Some(nested_schema) = &e.nested_schema {
24580            self.write_keyword("NESTED");
24581            if let Some(path) = &e.path {
24582                self.write_space();
24583                self.write_keyword("PATH");
24584                self.write_space();
24585                self.generate_expression(path)?;
24586            }
24587            self.write_space();
24588            self.generate_expression(nested_schema)?;
24589        } else {
24590            if let Some(this) = &e.this {
24591                self.generate_expression(this)?;
24592            }
24593            if let Some(kind) = &e.kind {
24594                self.write_space();
24595                self.write(kind);
24596            }
24597            if let Some(path) = &e.path {
24598                self.write_space();
24599                self.write_keyword("PATH");
24600                self.write_space();
24601                self.generate_expression(path)?;
24602            }
24603            if e.ordinality.is_some() {
24604                self.write_keyword(" FOR ORDINALITY");
24605            }
24606        }
24607        Ok(())
24608    }
24609
24610    fn generate_json_exists(&mut self, e: &JSONExists) -> Result<()> {
24611        // JSON_EXISTS(this, path PASSING vars ON ERROR/EMPTY condition)
24612        self.write_keyword("JSON_EXISTS");
24613        self.write("(");
24614        self.generate_expression(&e.this)?;
24615        if let Some(path) = &e.path {
24616            self.write(", ");
24617            self.generate_expression(path)?;
24618        }
24619        if let Some(passing) = &e.passing {
24620            self.write_space();
24621            self.write_keyword("PASSING");
24622            self.write_space();
24623            self.generate_expression(passing)?;
24624        }
24625        if let Some(on_condition) = &e.on_condition {
24626            self.write_space();
24627            self.generate_expression(on_condition)?;
24628        }
24629        self.write(")");
24630        Ok(())
24631    }
24632
24633    fn generate_json_cast(&mut self, e: &JSONCast) -> Result<()> {
24634        self.generate_expression(&e.this)?;
24635        self.write(".:");
24636        self.generate_data_type(&e.to)?;
24637        Ok(())
24638    }
24639
24640    fn generate_json_extract_array(&mut self, e: &JSONExtractArray) -> Result<()> {
24641        // JSON_EXTRACT_ARRAY(this, expression)
24642        self.write_keyword("JSON_EXTRACT_ARRAY");
24643        self.write("(");
24644        self.generate_expression(&e.this)?;
24645        if let Some(expr) = &e.expression {
24646            self.write(", ");
24647            self.generate_expression(expr)?;
24648        }
24649        self.write(")");
24650        Ok(())
24651    }
24652
24653    fn generate_json_extract_quote(&mut self, e: &JSONExtractQuote) -> Result<()> {
24654        // Snowflake: KEEP [OMIT] QUOTES [SCALAR_ONLY] for JSON extraction
24655        if let Some(option) = &e.option {
24656            self.generate_expression(option)?;
24657            self.write_space();
24658        }
24659        self.write_keyword("QUOTES");
24660        if e.scalar.is_some() {
24661            self.write_keyword(" SCALAR_ONLY");
24662        }
24663        Ok(())
24664    }
24665
24666    fn generate_json_extract_scalar(&mut self, e: &JSONExtractScalar) -> Result<()> {
24667        // JSON_EXTRACT_SCALAR(this, expression)
24668        self.write_keyword("JSON_EXTRACT_SCALAR");
24669        self.write("(");
24670        self.generate_expression(&e.this)?;
24671        self.write(", ");
24672        self.generate_expression(&e.expression)?;
24673        self.write(")");
24674        Ok(())
24675    }
24676
24677    fn generate_json_extract_path(&mut self, e: &JSONExtract) -> Result<()> {
24678        // For variant_extract (Snowflake/Databricks colon syntax like a:field)
24679        // Databricks uses col:path syntax, Snowflake uses GET_PATH(col, 'path')
24680        // Otherwise output JSON_EXTRACT(this, expression)
24681        if e.variant_extract.is_some() {
24682            use crate::dialects::DialectType;
24683            if matches!(self.config.dialect, Some(DialectType::Databricks)) {
24684                // Databricks: output col:path syntax (e.g., c1:price, c1:price.foo, c1:price.bar[1])
24685                self.generate_expression(&e.this)?;
24686                self.write(":");
24687                // The expression is a string literal containing the path (e.g., 'price' or 'price.foo')
24688                // We need to output it without quotes
24689                match e.expression.as_ref() {
24690                    Expression::Literal(Literal::String(s)) => {
24691                        self.write(s);
24692                    }
24693                    _ => {
24694                        // Fallback: generate as-is (shouldn't happen in typical cases)
24695                        self.generate_expression(&e.expression)?;
24696                    }
24697                }
24698            } else {
24699                // Snowflake and others: use GET_PATH(col, 'path')
24700                self.write_keyword("GET_PATH");
24701                self.write("(");
24702                self.generate_expression(&e.this)?;
24703                self.write(", ");
24704                self.generate_expression(&e.expression)?;
24705                self.write(")");
24706            }
24707        } else {
24708            self.write_keyword("JSON_EXTRACT");
24709            self.write("(");
24710            self.generate_expression(&e.this)?;
24711            self.write(", ");
24712            self.generate_expression(&e.expression)?;
24713            for expr in &e.expressions {
24714                self.write(", ");
24715                self.generate_expression(expr)?;
24716            }
24717            self.write(")");
24718        }
24719        Ok(())
24720    }
24721
24722    fn generate_json_format(&mut self, e: &JSONFormat) -> Result<()> {
24723        // Output: {expr} FORMAT JSON
24724        // This wraps an expression with FORMAT JSON suffix (Oracle JSON function syntax)
24725        if let Some(this) = &e.this {
24726            self.generate_expression(this)?;
24727            self.write_space();
24728        }
24729        self.write_keyword("FORMAT JSON");
24730        Ok(())
24731    }
24732
24733    fn generate_json_key_value(&mut self, e: &JSONKeyValue) -> Result<()> {
24734        // key: value (for JSON objects)
24735        self.generate_expression(&e.this)?;
24736        self.write(": ");
24737        self.generate_expression(&e.expression)?;
24738        Ok(())
24739    }
24740
24741    fn generate_json_keys(&mut self, e: &JSONKeys) -> Result<()> {
24742        // JSON_KEYS(this, expression, expressions...)
24743        self.write_keyword("JSON_KEYS");
24744        self.write("(");
24745        self.generate_expression(&e.this)?;
24746        if let Some(expr) = &e.expression {
24747            self.write(", ");
24748            self.generate_expression(expr)?;
24749        }
24750        for expr in &e.expressions {
24751            self.write(", ");
24752            self.generate_expression(expr)?;
24753        }
24754        self.write(")");
24755        Ok(())
24756    }
24757
24758    fn generate_json_keys_at_depth(&mut self, e: &JSONKeysAtDepth) -> Result<()> {
24759        // JSON_KEYS(this, expression)
24760        self.write_keyword("JSON_KEYS");
24761        self.write("(");
24762        self.generate_expression(&e.this)?;
24763        if let Some(expr) = &e.expression {
24764            self.write(", ");
24765            self.generate_expression(expr)?;
24766        }
24767        self.write(")");
24768        Ok(())
24769    }
24770
24771    fn generate_json_path_expr(&mut self, e: &JSONPath) -> Result<()> {
24772        // JSONPath expression: generates a quoted path like '$.foo' or '$[0]'
24773        // The path components are concatenated without spaces
24774        let mut path_str = String::new();
24775        for expr in &e.expressions {
24776            match expr {
24777                Expression::JSONPathRoot(_) => {
24778                    path_str.push('$');
24779                }
24780                Expression::JSONPathKey(k) => {
24781                    // .key or ."key" (quote if key has special characters)
24782                    if let Expression::Literal(crate::expressions::Literal::String(s)) = k.this.as_ref() {
24783                        path_str.push('.');
24784                        // Quote the key if it contains non-alphanumeric characters (hyphens, spaces, etc.)
24785                        let needs_quoting = s.chars().any(|c| !c.is_alphanumeric() && c != '_');
24786                        if needs_quoting {
24787                            path_str.push('"');
24788                            path_str.push_str(s);
24789                            path_str.push('"');
24790                        } else {
24791                            path_str.push_str(s);
24792                        }
24793                    }
24794                }
24795                Expression::JSONPathSubscript(s) => {
24796                    // [index]
24797                    if let Expression::Literal(crate::expressions::Literal::Number(n)) = s.this.as_ref() {
24798                        path_str.push('[');
24799                        path_str.push_str(n);
24800                        path_str.push(']');
24801                    }
24802                }
24803                _ => {
24804                    // For other path parts, try to generate them
24805                    let mut temp_gen = Self::with_config(self.config.clone());
24806                    temp_gen.generate_expression(expr)?;
24807                    path_str.push_str(&temp_gen.output);
24808                }
24809            }
24810        }
24811        // Output as quoted string
24812        self.write("'");
24813        self.write(&path_str);
24814        self.write("'");
24815        Ok(())
24816    }
24817
24818    fn generate_json_path_filter(&mut self, e: &JSONPathFilter) -> Result<()> {
24819        // JSON path filter: ?(predicate)
24820        self.write("?(");
24821        self.generate_expression(&e.this)?;
24822        self.write(")");
24823        Ok(())
24824    }
24825
24826    fn generate_json_path_key(&mut self, e: &JSONPathKey) -> Result<()> {
24827        // JSON path key: .key or ["key"]
24828        self.write(".");
24829        self.generate_expression(&e.this)?;
24830        Ok(())
24831    }
24832
24833    fn generate_json_path_recursive(&mut self, e: &JSONPathRecursive) -> Result<()> {
24834        // JSON path recursive descent: ..
24835        self.write("..");
24836        if let Some(this) = &e.this {
24837            self.generate_expression(this)?;
24838        }
24839        Ok(())
24840    }
24841
24842    fn generate_json_path_root(&mut self) -> Result<()> {
24843        // JSON path root: $
24844        self.write("$");
24845        Ok(())
24846    }
24847
24848    fn generate_json_path_script(&mut self, e: &JSONPathScript) -> Result<()> {
24849        // JSON path script: (expression)
24850        self.write("(");
24851        self.generate_expression(&e.this)?;
24852        self.write(")");
24853        Ok(())
24854    }
24855
24856    fn generate_json_path_selector(&mut self, e: &JSONPathSelector) -> Result<()> {
24857        // JSON path selector: *
24858        self.generate_expression(&e.this)?;
24859        Ok(())
24860    }
24861
24862    fn generate_json_path_slice(&mut self, e: &JSONPathSlice) -> Result<()> {
24863        // JSON path slice: [start:end:step]
24864        self.write("[");
24865        if let Some(start) = &e.start {
24866            self.generate_expression(start)?;
24867        }
24868        self.write(":");
24869        if let Some(end) = &e.end {
24870            self.generate_expression(end)?;
24871        }
24872        if let Some(step) = &e.step {
24873            self.write(":");
24874            self.generate_expression(step)?;
24875        }
24876        self.write("]");
24877        Ok(())
24878    }
24879
24880    fn generate_json_path_subscript(&mut self, e: &JSONPathSubscript) -> Result<()> {
24881        // JSON path subscript: [index] or [*]
24882        self.write("[");
24883        self.generate_expression(&e.this)?;
24884        self.write("]");
24885        Ok(())
24886    }
24887
24888    fn generate_json_path_union(&mut self, e: &JSONPathUnion) -> Result<()> {
24889        // JSON path union: [key1, key2, ...]
24890        self.write("[");
24891        for (i, expr) in e.expressions.iter().enumerate() {
24892            if i > 0 {
24893                self.write(", ");
24894            }
24895            self.generate_expression(expr)?;
24896        }
24897        self.write("]");
24898        Ok(())
24899    }
24900
24901    fn generate_json_remove(&mut self, e: &JSONRemove) -> Result<()> {
24902        // JSON_REMOVE(this, path1, path2, ...)
24903        self.write_keyword("JSON_REMOVE");
24904        self.write("(");
24905        self.generate_expression(&e.this)?;
24906        for expr in &e.expressions {
24907            self.write(", ");
24908            self.generate_expression(expr)?;
24909        }
24910        self.write(")");
24911        Ok(())
24912    }
24913
24914    fn generate_json_schema(&mut self, e: &JSONSchema) -> Result<()> {
24915        // COLUMNS(col1 type, col2 type, ...)
24916        // When pretty printing and content is too wide, format with each column on a separate line
24917        self.write_keyword("COLUMNS");
24918        self.write("(");
24919
24920        if self.config.pretty && !e.expressions.is_empty() {
24921            // First, generate all expressions into strings to check width
24922            let mut expr_strings: Vec<String> = Vec::with_capacity(e.expressions.len());
24923            for expr in &e.expressions {
24924                let mut temp_gen = Generator::with_config(self.config.clone());
24925                temp_gen.generate_expression(expr)?;
24926                expr_strings.push(temp_gen.output);
24927            }
24928
24929            // Check if total width exceeds max_text_width
24930            if self.too_wide(&expr_strings) {
24931                // Pretty print: each column on its own line
24932                self.write_newline();
24933                self.indent_level += 1;
24934                for (i, expr_str) in expr_strings.iter().enumerate() {
24935                    if i > 0 {
24936                        self.write(",");
24937                        self.write_newline();
24938                    }
24939                    self.write_indent();
24940                    self.write(expr_str);
24941                }
24942                self.write_newline();
24943                self.indent_level -= 1;
24944                self.write_indent();
24945            } else {
24946                // Compact: all on one line
24947                for (i, expr_str) in expr_strings.iter().enumerate() {
24948                    if i > 0 {
24949                        self.write(", ");
24950                    }
24951                    self.write(expr_str);
24952                }
24953            }
24954        } else {
24955            // Non-pretty mode: compact format
24956            for (i, expr) in e.expressions.iter().enumerate() {
24957                if i > 0 {
24958                    self.write(", ");
24959                }
24960                self.generate_expression(expr)?;
24961            }
24962        }
24963        self.write(")");
24964        Ok(())
24965    }
24966
24967    fn generate_json_set(&mut self, e: &JSONSet) -> Result<()> {
24968        // JSON_SET(this, path, value, ...)
24969        self.write_keyword("JSON_SET");
24970        self.write("(");
24971        self.generate_expression(&e.this)?;
24972        for expr in &e.expressions {
24973            self.write(", ");
24974            self.generate_expression(expr)?;
24975        }
24976        self.write(")");
24977        Ok(())
24978    }
24979
24980    fn generate_json_strip_nulls(&mut self, e: &JSONStripNulls) -> Result<()> {
24981        // JSON_STRIP_NULLS(this, expression)
24982        self.write_keyword("JSON_STRIP_NULLS");
24983        self.write("(");
24984        self.generate_expression(&e.this)?;
24985        if let Some(expr) = &e.expression {
24986            self.write(", ");
24987            self.generate_expression(expr)?;
24988        }
24989        self.write(")");
24990        Ok(())
24991    }
24992
24993    fn generate_json_table(&mut self, e: &JSONTable) -> Result<()> {
24994        // JSON_TABLE(this, path [error_handling] [empty_handling] schema)
24995        self.write_keyword("JSON_TABLE");
24996        self.write("(");
24997        self.generate_expression(&e.this)?;
24998        if let Some(path) = &e.path {
24999            self.write(", ");
25000            self.generate_expression(path)?;
25001        }
25002        if let Some(error_handling) = &e.error_handling {
25003            self.write_space();
25004            self.generate_expression(error_handling)?;
25005        }
25006        if let Some(empty_handling) = &e.empty_handling {
25007            self.write_space();
25008            self.generate_expression(empty_handling)?;
25009        }
25010        if let Some(schema) = &e.schema {
25011            self.write_space();
25012            self.generate_expression(schema)?;
25013        }
25014        self.write(")");
25015        Ok(())
25016    }
25017
25018    fn generate_json_type(&mut self, e: &JSONType) -> Result<()> {
25019        // JSON_TYPE(this)
25020        self.write_keyword("JSON_TYPE");
25021        self.write("(");
25022        self.generate_expression(&e.this)?;
25023        self.write(")");
25024        Ok(())
25025    }
25026
25027    fn generate_json_value(&mut self, e: &JSONValue) -> Result<()> {
25028        // JSON_VALUE(this, path RETURNING type ON condition)
25029        self.write_keyword("JSON_VALUE");
25030        self.write("(");
25031        self.generate_expression(&e.this)?;
25032        if let Some(path) = &e.path {
25033            self.write(", ");
25034            self.generate_expression(path)?;
25035        }
25036        if let Some(returning) = &e.returning {
25037            self.write_space();
25038            self.write_keyword("RETURNING");
25039            self.write_space();
25040            self.generate_expression(returning)?;
25041        }
25042        if let Some(on_condition) = &e.on_condition {
25043            self.write_space();
25044            self.generate_expression(on_condition)?;
25045        }
25046        self.write(")");
25047        Ok(())
25048    }
25049
25050    fn generate_json_value_array(&mut self, e: &JSONValueArray) -> Result<()> {
25051        // JSON_VALUE_ARRAY(this)
25052        self.write_keyword("JSON_VALUE_ARRAY");
25053        self.write("(");
25054        self.generate_expression(&e.this)?;
25055        self.write(")");
25056        Ok(())
25057    }
25058
25059    fn generate_jarowinkler_similarity(&mut self, e: &JarowinklerSimilarity) -> Result<()> {
25060        // JAROWINKLER_SIMILARITY(str1, str2)
25061        self.write_keyword("JAROWINKLER_SIMILARITY");
25062        self.write("(");
25063        self.generate_expression(&e.this)?;
25064        self.write(", ");
25065        self.generate_expression(&e.expression)?;
25066        self.write(")");
25067        Ok(())
25068    }
25069
25070    fn generate_join_hint(&mut self, e: &JoinHint) -> Result<()> {
25071        // Python: this(expressions)
25072        self.generate_expression(&e.this)?;
25073        self.write("(");
25074        for (i, expr) in e.expressions.iter().enumerate() {
25075            if i > 0 {
25076                self.write(", ");
25077            }
25078            self.generate_expression(expr)?;
25079        }
25080        self.write(")");
25081        Ok(())
25082    }
25083
25084    fn generate_journal_property(&mut self, e: &JournalProperty) -> Result<()> {
25085        // Python: {no}{local}{dual}{before}{after}JOURNAL
25086        if e.no.is_some() {
25087            self.write_keyword("NO ");
25088        }
25089        if let Some(local) = &e.local {
25090            self.generate_expression(local)?;
25091            self.write_space();
25092        }
25093        if e.dual.is_some() {
25094            self.write_keyword("DUAL ");
25095        }
25096        if e.before.is_some() {
25097            self.write_keyword("BEFORE ");
25098        }
25099        if e.after.is_some() {
25100            self.write_keyword("AFTER ");
25101        }
25102        self.write_keyword("JOURNAL");
25103        Ok(())
25104    }
25105
25106    fn generate_language_property(&mut self, e: &LanguageProperty) -> Result<()> {
25107        // LANGUAGE language_name
25108        self.write_keyword("LANGUAGE");
25109        self.write_space();
25110        self.generate_expression(&e.this)?;
25111        Ok(())
25112    }
25113
25114    fn generate_lateral(&mut self, e: &Lateral) -> Result<()> {
25115        // Python: handles LATERAL VIEW (Hive/Spark) and regular LATERAL
25116        if e.view.is_some() {
25117            // LATERAL VIEW [OUTER] expression [alias] [AS columns]
25118            self.write_keyword("LATERAL VIEW");
25119            if e.outer.is_some() {
25120                self.write_space();
25121                self.write_keyword("OUTER");
25122            }
25123            self.write_space();
25124            self.generate_expression(&e.this)?;
25125            if let Some(alias) = &e.alias {
25126                self.write_space();
25127                self.write(alias);
25128            }
25129        } else {
25130            // LATERAL subquery/function [WITH ORDINALITY] [AS alias(columns)]
25131            self.write_keyword("LATERAL");
25132            self.write_space();
25133            self.generate_expression(&e.this)?;
25134            if e.ordinality.is_some() {
25135                self.write_space();
25136                self.write_keyword("WITH ORDINALITY");
25137            }
25138            if let Some(alias) = &e.alias {
25139                self.write_space();
25140                self.write_keyword("AS");
25141                self.write_space();
25142                self.write(alias);
25143                if !e.column_aliases.is_empty() {
25144                    self.write("(");
25145                    for (i, col) in e.column_aliases.iter().enumerate() {
25146                        if i > 0 {
25147                            self.write(", ");
25148                        }
25149                        self.write(col);
25150                    }
25151                    self.write(")");
25152                }
25153            }
25154        }
25155        Ok(())
25156    }
25157
25158    fn generate_like_property(&mut self, e: &LikeProperty) -> Result<()> {
25159        // Python: LIKE this [options]
25160        self.write_keyword("LIKE");
25161        self.write_space();
25162        self.generate_expression(&e.this)?;
25163        for expr in &e.expressions {
25164            self.write_space();
25165            self.generate_expression(expr)?;
25166        }
25167        Ok(())
25168    }
25169
25170    fn generate_limit(&mut self, e: &Limit) -> Result<()> {
25171        self.write_keyword("LIMIT");
25172        self.write_space();
25173        self.write_limit_expr(&e.this)?;
25174        if e.percent {
25175            self.write_space();
25176            self.write_keyword("PERCENT");
25177        }
25178        Ok(())
25179    }
25180
25181    fn generate_limit_options(&mut self, e: &LimitOptions) -> Result<()> {
25182        // Python: [PERCENT][ROWS][WITH TIES|ONLY]
25183        if e.percent.is_some() {
25184            self.write_keyword(" PERCENT");
25185        }
25186        if e.rows.is_some() {
25187            self.write_keyword(" ROWS");
25188        }
25189        if e.with_ties.is_some() {
25190            self.write_keyword(" WITH TIES");
25191        } else if e.rows.is_some() {
25192            self.write_keyword(" ONLY");
25193        }
25194        Ok(())
25195    }
25196
25197    fn generate_list(&mut self, e: &List) -> Result<()> {
25198        use crate::dialects::DialectType;
25199        let is_materialize = matches!(self.config.dialect, Some(DialectType::Materialize));
25200
25201        // Check if this is a subquery-based list (LIST(SELECT ...))
25202        if e.expressions.len() == 1 {
25203            if let Expression::Select(_) = &e.expressions[0] {
25204                self.write_keyword("LIST");
25205                self.write("(");
25206                self.generate_expression(&e.expressions[0])?;
25207                self.write(")");
25208                return Ok(());
25209            }
25210        }
25211
25212        // For Materialize, output as LIST[expr, expr, ...]
25213        if is_materialize {
25214            self.write_keyword("LIST");
25215            self.write("[");
25216            for (i, expr) in e.expressions.iter().enumerate() {
25217                if i > 0 {
25218                    self.write(", ");
25219                }
25220                self.generate_expression(expr)?;
25221            }
25222            self.write("]");
25223        } else {
25224            // For other dialects, output as LIST(expr, expr, ...)
25225            self.write_keyword("LIST");
25226            self.write("(");
25227            for (i, expr) in e.expressions.iter().enumerate() {
25228                if i > 0 {
25229                    self.write(", ");
25230                }
25231                self.generate_expression(expr)?;
25232            }
25233            self.write(")");
25234        }
25235        Ok(())
25236    }
25237
25238    fn generate_tomap(&mut self, e: &ToMap) -> Result<()> {
25239        // Check if this is a subquery-based map (MAP(SELECT ...))
25240        if let Expression::Select(_) = &*e.this {
25241            self.write_keyword("MAP");
25242            self.write("(");
25243            self.generate_expression(&e.this)?;
25244            self.write(")");
25245            return Ok(());
25246        }
25247
25248        let is_duckdb = matches!(self.config.dialect, Some(DialectType::DuckDB));
25249
25250        // For Struct-based map: DuckDB uses MAP {'key': value}, Materialize uses MAP['key' => value]
25251        self.write_keyword("MAP");
25252        if is_duckdb {
25253            self.write(" {");
25254        } else {
25255            self.write("[");
25256        }
25257        if let Expression::Struct(s) = &*e.this {
25258            for (i, (_, expr)) in s.fields.iter().enumerate() {
25259                if i > 0 {
25260                    self.write(", ");
25261                }
25262                if let Expression::PropertyEQ(op) = expr {
25263                    self.generate_expression(&op.left)?;
25264                    if is_duckdb {
25265                        self.write(": ");
25266                    } else {
25267                        self.write(" => ");
25268                    }
25269                    self.generate_expression(&op.right)?;
25270                } else {
25271                    self.generate_expression(expr)?;
25272                }
25273            }
25274        }
25275        if is_duckdb {
25276            self.write("}");
25277        } else {
25278            self.write("]");
25279        }
25280        Ok(())
25281    }
25282
25283    fn generate_localtime(&mut self, e: &Localtime) -> Result<()> {
25284        // Python: LOCALTIME or LOCALTIME(precision)
25285        self.write_keyword("LOCALTIME");
25286        if let Some(precision) = &e.this {
25287            self.write("(");
25288            self.generate_expression(precision)?;
25289            self.write(")");
25290        }
25291        Ok(())
25292    }
25293
25294    fn generate_localtimestamp(&mut self, e: &Localtimestamp) -> Result<()> {
25295        // Python: LOCALTIMESTAMP or LOCALTIMESTAMP(precision)
25296        self.write_keyword("LOCALTIMESTAMP");
25297        if let Some(precision) = &e.this {
25298            self.write("(");
25299            self.generate_expression(precision)?;
25300            self.write(")");
25301        }
25302        Ok(())
25303    }
25304
25305    fn generate_location_property(&mut self, e: &LocationProperty) -> Result<()> {
25306        // LOCATION 'path'
25307        self.write_keyword("LOCATION");
25308        self.write_space();
25309        self.generate_expression(&e.this)?;
25310        Ok(())
25311    }
25312
25313    fn generate_lock(&mut self, e: &Lock) -> Result<()> {
25314        // Python: FOR UPDATE|FOR SHARE [OF tables] [NOWAIT|WAIT n]
25315        if e.update.is_some() {
25316            if e.key.is_some() {
25317                self.write_keyword("FOR NO KEY UPDATE");
25318            } else {
25319                self.write_keyword("FOR UPDATE");
25320            }
25321        } else {
25322            if e.key.is_some() {
25323                self.write_keyword("FOR KEY SHARE");
25324            } else {
25325                self.write_keyword("FOR SHARE");
25326            }
25327        }
25328        if !e.expressions.is_empty() {
25329            self.write_keyword(" OF ");
25330            for (i, expr) in e.expressions.iter().enumerate() {
25331                if i > 0 {
25332                    self.write(", ");
25333                }
25334                self.generate_expression(expr)?;
25335            }
25336        }
25337        // Handle wait option following Python sqlglot convention:
25338        // - Boolean(true) -> NOWAIT
25339        // - Boolean(false) -> SKIP LOCKED
25340        // - Literal (number) -> WAIT n
25341        if let Some(wait) = &e.wait {
25342            match wait.as_ref() {
25343                Expression::Boolean(b) => {
25344                    if b.value {
25345                        self.write_keyword(" NOWAIT");
25346                    } else {
25347                        self.write_keyword(" SKIP LOCKED");
25348                    }
25349                }
25350                _ => {
25351                    // It's a literal (number), output WAIT n
25352                    self.write_keyword(" WAIT ");
25353                    self.generate_expression(wait)?;
25354                }
25355            }
25356        }
25357        Ok(())
25358    }
25359
25360    fn generate_lock_property(&mut self, e: &LockProperty) -> Result<()> {
25361        // LOCK property
25362        self.write_keyword("LOCK");
25363        self.write_space();
25364        self.generate_expression(&e.this)?;
25365        Ok(())
25366    }
25367
25368    fn generate_locking_property(&mut self, e: &LockingProperty) -> Result<()> {
25369        // Python: LOCKING kind [this] [for_or_in] lock_type [OVERRIDE]
25370        self.write_keyword("LOCKING");
25371        self.write_space();
25372        self.write(&e.kind);
25373        if let Some(this) = &e.this {
25374            self.write_space();
25375            self.generate_expression(this)?;
25376        }
25377        if let Some(for_or_in) = &e.for_or_in {
25378            self.write_space();
25379            self.generate_expression(for_or_in)?;
25380        }
25381        if let Some(lock_type) = &e.lock_type {
25382            self.write_space();
25383            self.generate_expression(lock_type)?;
25384        }
25385        if e.override_.is_some() {
25386            self.write_keyword(" OVERRIDE");
25387        }
25388        Ok(())
25389    }
25390
25391    fn generate_locking_statement(&mut self, e: &LockingStatement) -> Result<()> {
25392        // this expression
25393        self.generate_expression(&e.this)?;
25394        self.write_space();
25395        self.generate_expression(&e.expression)?;
25396        Ok(())
25397    }
25398
25399    fn generate_log_property(&mut self, e: &LogProperty) -> Result<()> {
25400        // [NO] LOG
25401        if e.no.is_some() {
25402            self.write_keyword("NO ");
25403        }
25404        self.write_keyword("LOG");
25405        Ok(())
25406    }
25407
25408    fn generate_md5_digest(&mut self, e: &MD5Digest) -> Result<()> {
25409        // MD5(this, expressions...)
25410        self.write_keyword("MD5");
25411        self.write("(");
25412        self.generate_expression(&e.this)?;
25413        for expr in &e.expressions {
25414            self.write(", ");
25415            self.generate_expression(expr)?;
25416        }
25417        self.write(")");
25418        Ok(())
25419    }
25420
25421    fn generate_ml_forecast(&mut self, e: &MLForecast) -> Result<()> {
25422        // ML.FORECAST(model, [params])
25423        self.write_keyword("ML.FORECAST");
25424        self.write("(");
25425        self.generate_expression(&e.this)?;
25426        if let Some(expression) = &e.expression {
25427            self.write(", ");
25428            self.generate_expression(expression)?;
25429        }
25430        if let Some(params) = &e.params_struct {
25431            self.write(", ");
25432            self.generate_expression(params)?;
25433        }
25434        self.write(")");
25435        Ok(())
25436    }
25437
25438    fn generate_ml_translate(&mut self, e: &MLTranslate) -> Result<()> {
25439        // ML.TRANSLATE(model, input, [params])
25440        self.write_keyword("ML.TRANSLATE");
25441        self.write("(");
25442        self.generate_expression(&e.this)?;
25443        self.write(", ");
25444        self.generate_expression(&e.expression)?;
25445        if let Some(params) = &e.params_struct {
25446            self.write(", ");
25447            self.generate_expression(params)?;
25448        }
25449        self.write(")");
25450        Ok(())
25451    }
25452
25453    fn generate_make_interval(&mut self, e: &MakeInterval) -> Result<()> {
25454        // MAKE_INTERVAL(years => x, months => y, ...)
25455        self.write_keyword("MAKE_INTERVAL");
25456        self.write("(");
25457        let mut first = true;
25458        if let Some(year) = &e.year {
25459            self.write("years => ");
25460            self.generate_expression(year)?;
25461            first = false;
25462        }
25463        if let Some(month) = &e.month {
25464            if !first { self.write(", "); }
25465            self.write("months => ");
25466            self.generate_expression(month)?;
25467            first = false;
25468        }
25469        if let Some(week) = &e.week {
25470            if !first { self.write(", "); }
25471            self.write("weeks => ");
25472            self.generate_expression(week)?;
25473            first = false;
25474        }
25475        if let Some(day) = &e.day {
25476            if !first { self.write(", "); }
25477            self.write("days => ");
25478            self.generate_expression(day)?;
25479            first = false;
25480        }
25481        if let Some(hour) = &e.hour {
25482            if !first { self.write(", "); }
25483            self.write("hours => ");
25484            self.generate_expression(hour)?;
25485            first = false;
25486        }
25487        if let Some(minute) = &e.minute {
25488            if !first { self.write(", "); }
25489            self.write("mins => ");
25490            self.generate_expression(minute)?;
25491            first = false;
25492        }
25493        if let Some(second) = &e.second {
25494            if !first { self.write(", "); }
25495            self.write("secs => ");
25496            self.generate_expression(second)?;
25497        }
25498        self.write(")");
25499        Ok(())
25500    }
25501
25502    fn generate_manhattan_distance(&mut self, e: &ManhattanDistance) -> Result<()> {
25503        // MANHATTAN_DISTANCE(vector1, vector2)
25504        self.write_keyword("MANHATTAN_DISTANCE");
25505        self.write("(");
25506        self.generate_expression(&e.this)?;
25507        self.write(", ");
25508        self.generate_expression(&e.expression)?;
25509        self.write(")");
25510        Ok(())
25511    }
25512
25513    fn generate_map(&mut self, e: &Map) -> Result<()> {
25514        // MAP(key1, value1, key2, value2, ...)
25515        self.write_keyword("MAP");
25516        self.write("(");
25517        for (i, (key, value)) in e.keys.iter().zip(e.values.iter()).enumerate() {
25518            if i > 0 {
25519                self.write(", ");
25520            }
25521            self.generate_expression(key)?;
25522            self.write(", ");
25523            self.generate_expression(value)?;
25524        }
25525        self.write(")");
25526        Ok(())
25527    }
25528
25529    fn generate_map_cat(&mut self, e: &MapCat) -> Result<()> {
25530        // MAP_CAT(map1, map2)
25531        self.write_keyword("MAP_CAT");
25532        self.write("(");
25533        self.generate_expression(&e.this)?;
25534        self.write(", ");
25535        self.generate_expression(&e.expression)?;
25536        self.write(")");
25537        Ok(())
25538    }
25539
25540    fn generate_map_delete(&mut self, e: &MapDelete) -> Result<()> {
25541        // MAP_DELETE(map, key1, key2, ...)
25542        self.write_keyword("MAP_DELETE");
25543        self.write("(");
25544        self.generate_expression(&e.this)?;
25545        for expr in &e.expressions {
25546            self.write(", ");
25547            self.generate_expression(expr)?;
25548        }
25549        self.write(")");
25550        Ok(())
25551    }
25552
25553    fn generate_map_insert(&mut self, e: &MapInsert) -> Result<()> {
25554        // MAP_INSERT(map, key, value, [update_flag])
25555        self.write_keyword("MAP_INSERT");
25556        self.write("(");
25557        self.generate_expression(&e.this)?;
25558        if let Some(key) = &e.key {
25559            self.write(", ");
25560            self.generate_expression(key)?;
25561        }
25562        if let Some(value) = &e.value {
25563            self.write(", ");
25564            self.generate_expression(value)?;
25565        }
25566        if let Some(update_flag) = &e.update_flag {
25567            self.write(", ");
25568            self.generate_expression(update_flag)?;
25569        }
25570        self.write(")");
25571        Ok(())
25572    }
25573
25574    fn generate_map_pick(&mut self, e: &MapPick) -> Result<()> {
25575        // MAP_PICK(map, key1, key2, ...)
25576        self.write_keyword("MAP_PICK");
25577        self.write("(");
25578        self.generate_expression(&e.this)?;
25579        for expr in &e.expressions {
25580            self.write(", ");
25581            self.generate_expression(expr)?;
25582        }
25583        self.write(")");
25584        Ok(())
25585    }
25586
25587    fn generate_masking_policy_column_constraint(&mut self, e: &MaskingPolicyColumnConstraint) -> Result<()> {
25588        // Python: MASKING POLICY name [USING (cols)]
25589        self.write_keyword("MASKING POLICY");
25590        self.write_space();
25591        self.generate_expression(&e.this)?;
25592        if !e.expressions.is_empty() {
25593            self.write_keyword(" USING");
25594            self.write(" (");
25595            for (i, expr) in e.expressions.iter().enumerate() {
25596                if i > 0 {
25597                    self.write(", ");
25598                }
25599                self.generate_expression(expr)?;
25600            }
25601            self.write(")");
25602        }
25603        Ok(())
25604    }
25605
25606    fn generate_match_against(&mut self, e: &MatchAgainst) -> Result<()> {
25607        if matches!(
25608            self.config.dialect,
25609            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)
25610        ) {
25611            if e.expressions.len() > 1 {
25612                self.write("(");
25613            }
25614            for (i, expr) in e.expressions.iter().enumerate() {
25615                if i > 0 {
25616                    self.write_keyword(" OR ");
25617                }
25618                self.generate_expression(expr)?;
25619                self.write_space();
25620                self.write("@@");
25621                self.write_space();
25622                self.generate_expression(&e.this)?;
25623            }
25624            if e.expressions.len() > 1 {
25625                self.write(")");
25626            }
25627            return Ok(());
25628        }
25629
25630        // MATCH(columns) AGAINST(expr [modifier])
25631        self.write_keyword("MATCH");
25632        self.write("(");
25633        for (i, expr) in e.expressions.iter().enumerate() {
25634            if i > 0 {
25635                self.write(", ");
25636            }
25637            self.generate_expression(expr)?;
25638        }
25639        self.write(")");
25640        self.write_keyword(" AGAINST");
25641        self.write("(");
25642        self.generate_expression(&e.this)?;
25643        if let Some(modifier) = &e.modifier {
25644            self.write_space();
25645            self.generate_expression(modifier)?;
25646        }
25647        self.write(")");
25648        Ok(())
25649    }
25650
25651    fn generate_match_recognize_measure(&mut self, e: &MatchRecognizeMeasure) -> Result<()> {
25652        // Python: [window_frame] this
25653        if let Some(window_frame) = &e.window_frame {
25654            self.write(&format!("{:?}", window_frame).to_uppercase());
25655            self.write_space();
25656        }
25657        self.generate_expression(&e.this)?;
25658        Ok(())
25659    }
25660
25661    fn generate_materialized_property(&mut self, e: &MaterializedProperty) -> Result<()> {
25662        // MATERIALIZED [this]
25663        self.write_keyword("MATERIALIZED");
25664        if let Some(this) = &e.this {
25665            self.write_space();
25666            self.generate_expression(this)?;
25667        }
25668        Ok(())
25669    }
25670
25671    fn generate_merge(&mut self, e: &Merge) -> Result<()> {
25672        // MERGE INTO target USING source ON condition WHEN ...
25673        // DuckDB variant: MERGE INTO target USING source USING (key_columns) WHEN ...
25674        if let Some(with_) = &e.with_ {
25675            self.generate_expression(with_)?;
25676            self.write_space();
25677        }
25678        self.write_keyword("MERGE INTO");
25679        self.write_space();
25680        self.generate_expression(&e.this)?;
25681
25682        // USING clause - newline before in pretty mode
25683        if self.config.pretty {
25684            self.write_newline();
25685            self.write_indent();
25686        } else {
25687            self.write_space();
25688        }
25689        self.write_keyword("USING");
25690        self.write_space();
25691        self.generate_expression(&e.using)?;
25692
25693        // ON clause - newline before in pretty mode
25694        if let Some(on) = &e.on {
25695            if self.config.pretty {
25696                self.write_newline();
25697                self.write_indent();
25698            } else {
25699                self.write_space();
25700            }
25701            self.write_keyword("ON");
25702            self.write_space();
25703            self.generate_expression(on)?;
25704        }
25705        // DuckDB USING (key_columns) clause
25706        if let Some(using_cond) = &e.using_cond {
25707            self.write_space();
25708            self.write_keyword("USING");
25709            self.write_space();
25710            self.write("(");
25711            // using_cond is a Tuple containing the column identifiers
25712            if let Expression::Tuple(tuple) = using_cond.as_ref() {
25713                for (i, col) in tuple.expressions.iter().enumerate() {
25714                    if i > 0 {
25715                        self.write(", ");
25716                    }
25717                    self.generate_expression(col)?;
25718                }
25719            } else {
25720                self.generate_expression(using_cond)?;
25721            }
25722            self.write(")");
25723        }
25724        // For PostgreSQL dialect, extract target table name/alias to strip from UPDATE SET
25725        let saved_merge_strip = std::mem::take(&mut self.merge_strip_qualifiers);
25726        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)) {
25727            let mut names = Vec::new();
25728            match e.this.as_ref() {
25729                Expression::Alias(a) => {
25730                    // e.g., "x AS z" -> strip both "x" and "z"
25731                    if let Expression::Table(t) = &a.this {
25732                        names.push(t.name.name.clone());
25733                    } else if let Expression::Identifier(id) = &a.this {
25734                        names.push(id.name.clone());
25735                    }
25736                    names.push(a.alias.name.clone());
25737                }
25738                Expression::Table(t) => {
25739                    names.push(t.name.name.clone());
25740                }
25741                Expression::Identifier(id) => {
25742                    names.push(id.name.clone());
25743                }
25744                _ => {}
25745            }
25746            self.merge_strip_qualifiers = names;
25747        }
25748
25749        // WHEN clauses - newline before each in pretty mode
25750        if let Some(whens) = &e.whens {
25751            if self.config.pretty {
25752                self.write_newline();
25753                self.write_indent();
25754            } else {
25755                self.write_space();
25756            }
25757            self.generate_expression(whens)?;
25758        }
25759
25760        // Restore merge_strip_qualifiers
25761        self.merge_strip_qualifiers = saved_merge_strip;
25762
25763        // OUTPUT/RETURNING clause - newline before in pretty mode
25764        if let Some(returning) = &e.returning {
25765            if self.config.pretty {
25766                self.write_newline();
25767                self.write_indent();
25768            } else {
25769                self.write_space();
25770            }
25771            self.generate_expression(returning)?;
25772        }
25773        Ok(())
25774    }
25775
25776    fn generate_merge_block_ratio_property(&mut self, e: &MergeBlockRatioProperty) -> Result<()> {
25777        // Python: NO MERGEBLOCKRATIO | DEFAULT MERGEBLOCKRATIO | MERGEBLOCKRATIO=this [PERCENT]
25778        if e.no.is_some() {
25779            self.write_keyword("NO MERGEBLOCKRATIO");
25780        } else if e.default.is_some() {
25781            self.write_keyword("DEFAULT MERGEBLOCKRATIO");
25782        } else {
25783            self.write_keyword("MERGEBLOCKRATIO");
25784            self.write("=");
25785            if let Some(this) = &e.this {
25786                self.generate_expression(this)?;
25787            }
25788            if e.percent.is_some() {
25789                self.write_keyword(" PERCENT");
25790            }
25791        }
25792        Ok(())
25793    }
25794
25795    fn generate_merge_tree_ttl(&mut self, e: &MergeTreeTTL) -> Result<()> {
25796        // TTL expressions [WHERE where] [GROUP BY group] [SET aggregates]
25797        self.write_keyword("TTL");
25798        let pretty_clickhouse = self.config.pretty
25799            && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse));
25800
25801        if pretty_clickhouse {
25802            self.write_newline();
25803            self.indent_level += 1;
25804            for (i, expr) in e.expressions.iter().enumerate() {
25805                if i > 0 {
25806                    self.write(",");
25807                    self.write_newline();
25808                }
25809                self.write_indent();
25810                self.generate_expression(expr)?;
25811            }
25812            self.indent_level -= 1;
25813        } else {
25814            self.write_space();
25815            for (i, expr) in e.expressions.iter().enumerate() {
25816                if i > 0 {
25817                    self.write(", ");
25818                }
25819                self.generate_expression(expr)?;
25820            }
25821        }
25822
25823        if let Some(where_) = &e.where_ {
25824            if pretty_clickhouse {
25825                self.write_newline();
25826                if let Expression::Where(w) = where_.as_ref() {
25827                    self.write_indent();
25828                    self.write_keyword("WHERE");
25829                    self.write_newline();
25830                    self.indent_level += 1;
25831                    self.write_indent();
25832                    self.generate_expression(&w.this)?;
25833                    self.indent_level -= 1;
25834                } else {
25835                    self.write_indent();
25836                    self.generate_expression(where_)?;
25837                }
25838            } else {
25839                self.write_space();
25840                self.generate_expression(where_)?;
25841            }
25842        }
25843        if let Some(group) = &e.group {
25844            if pretty_clickhouse {
25845                self.write_newline();
25846                if let Expression::Group(g) = group.as_ref() {
25847                    self.write_indent();
25848                    self.write_keyword("GROUP BY");
25849                    self.write_newline();
25850                    self.indent_level += 1;
25851                    for (i, expr) in g.expressions.iter().enumerate() {
25852                        if i > 0 {
25853                            self.write(",");
25854                            self.write_newline();
25855                        }
25856                        self.write_indent();
25857                        self.generate_expression(expr)?;
25858                    }
25859                    self.indent_level -= 1;
25860                } else {
25861                    self.write_indent();
25862                    self.generate_expression(group)?;
25863                }
25864            } else {
25865                self.write_space();
25866                self.generate_expression(group)?;
25867            }
25868        }
25869        if let Some(aggregates) = &e.aggregates {
25870            if pretty_clickhouse {
25871                self.write_newline();
25872                self.write_indent();
25873                self.write_keyword("SET");
25874                self.write_newline();
25875                self.indent_level += 1;
25876                if let Expression::Tuple(t) = aggregates.as_ref() {
25877                    for (i, agg) in t.expressions.iter().enumerate() {
25878                        if i > 0 {
25879                            self.write(",");
25880                            self.write_newline();
25881                        }
25882                        self.write_indent();
25883                        self.generate_expression(agg)?;
25884                    }
25885                } else {
25886                    self.write_indent();
25887                    self.generate_expression(aggregates)?;
25888                }
25889                self.indent_level -= 1;
25890            } else {
25891                self.write_space();
25892                self.write_keyword("SET");
25893                self.write_space();
25894                self.generate_expression(aggregates)?;
25895            }
25896        }
25897        Ok(())
25898    }
25899
25900    fn generate_merge_tree_ttl_action(&mut self, e: &MergeTreeTTLAction) -> Result<()> {
25901        // Python: this [DELETE] [RECOMPRESS codec] [TO DISK disk] [TO VOLUME volume]
25902        self.generate_expression(&e.this)?;
25903        if e.delete.is_some() {
25904            self.write_keyword(" DELETE");
25905        }
25906        if let Some(recompress) = &e.recompress {
25907            self.write_keyword(" RECOMPRESS ");
25908            self.generate_expression(recompress)?;
25909        }
25910        if let Some(to_disk) = &e.to_disk {
25911            self.write_keyword(" TO DISK ");
25912            self.generate_expression(to_disk)?;
25913        }
25914        if let Some(to_volume) = &e.to_volume {
25915            self.write_keyword(" TO VOLUME ");
25916            self.generate_expression(to_volume)?;
25917        }
25918        Ok(())
25919    }
25920
25921    fn generate_minhash(&mut self, e: &Minhash) -> Result<()> {
25922        // MINHASH(this, expressions...)
25923        self.write_keyword("MINHASH");
25924        self.write("(");
25925        self.generate_expression(&e.this)?;
25926        for expr in &e.expressions {
25927            self.write(", ");
25928            self.generate_expression(expr)?;
25929        }
25930        self.write(")");
25931        Ok(())
25932    }
25933
25934    fn generate_model_attribute(&mut self, e: &ModelAttribute) -> Result<()> {
25935        // model!attribute - Snowflake syntax
25936        self.generate_expression(&e.this)?;
25937        self.write("!");
25938        self.generate_expression(&e.expression)?;
25939        Ok(())
25940    }
25941
25942    fn generate_monthname(&mut self, e: &Monthname) -> Result<()> {
25943        // MONTHNAME(this)
25944        self.write_keyword("MONTHNAME");
25945        self.write("(");
25946        self.generate_expression(&e.this)?;
25947        self.write(")");
25948        Ok(())
25949    }
25950
25951    fn generate_multitable_inserts(&mut self, e: &MultitableInserts) -> Result<()> {
25952        // Output leading comments
25953        for comment in &e.leading_comments {
25954            self.write_formatted_comment(comment);
25955            self.write_space();
25956        }
25957        // Python: INSERT kind expressions source
25958        self.write_keyword("INSERT");
25959        self.write_space();
25960        self.write(&e.kind);
25961        for expr in &e.expressions {
25962            self.write_space();
25963            self.generate_expression(expr)?;
25964        }
25965        if let Some(source) = &e.source {
25966            self.write_space();
25967            self.generate_expression(source)?;
25968        }
25969        Ok(())
25970    }
25971
25972    fn generate_next_value_for(&mut self, e: &NextValueFor) -> Result<()> {
25973        // Python: NEXT VALUE FOR this [OVER (order)]
25974        self.write_keyword("NEXT VALUE FOR");
25975        self.write_space();
25976        self.generate_expression(&e.this)?;
25977        if let Some(order) = &e.order {
25978            self.write_space();
25979            self.write_keyword("OVER");
25980            self.write(" (");
25981            self.generate_expression(order)?;
25982            self.write(")");
25983        }
25984        Ok(())
25985    }
25986
25987    fn generate_normal(&mut self, e: &Normal) -> Result<()> {
25988        // NORMAL(mean, stddev, gen)
25989        self.write_keyword("NORMAL");
25990        self.write("(");
25991        self.generate_expression(&e.this)?;
25992        if let Some(stddev) = &e.stddev {
25993            self.write(", ");
25994            self.generate_expression(stddev)?;
25995        }
25996        if let Some(gen) = &e.gen {
25997            self.write(", ");
25998            self.generate_expression(gen)?;
25999        }
26000        self.write(")");
26001        Ok(())
26002    }
26003
26004    fn generate_normalize(&mut self, e: &Normalize) -> Result<()> {
26005        // NORMALIZE(this, form) or CASEFOLD version
26006        if e.is_casefold.is_some() {
26007            self.write_keyword("NORMALIZE_AND_CASEFOLD");
26008        } else {
26009            self.write_keyword("NORMALIZE");
26010        }
26011        self.write("(");
26012        self.generate_expression(&e.this)?;
26013        if let Some(form) = &e.form {
26014            self.write(", ");
26015            self.generate_expression(form)?;
26016        }
26017        self.write(")");
26018        Ok(())
26019    }
26020
26021    fn generate_not_null_column_constraint(&mut self, e: &NotNullColumnConstraint) -> Result<()> {
26022        // Python: [NOT ]NULL
26023        if e.allow_null.is_none() {
26024            self.write_keyword("NOT ");
26025        }
26026        self.write_keyword("NULL");
26027        Ok(())
26028    }
26029
26030    fn generate_nullif(&mut self, e: &Nullif) -> Result<()> {
26031        // NULLIF(this, expression)
26032        self.write_keyword("NULLIF");
26033        self.write("(");
26034        self.generate_expression(&e.this)?;
26035        self.write(", ");
26036        self.generate_expression(&e.expression)?;
26037        self.write(")");
26038        Ok(())
26039    }
26040
26041    fn generate_number_to_str(&mut self, e: &NumberToStr) -> Result<()> {
26042        // FORMAT(this, format, culture)
26043        self.write_keyword("FORMAT");
26044        self.write("(");
26045        self.generate_expression(&e.this)?;
26046        self.write(", '");
26047        self.write(&e.format);
26048        self.write("'");
26049        if let Some(culture) = &e.culture {
26050            self.write(", ");
26051            self.generate_expression(culture)?;
26052        }
26053        self.write(")");
26054        Ok(())
26055    }
26056
26057    fn generate_object_agg(&mut self, e: &ObjectAgg) -> Result<()> {
26058        // OBJECT_AGG(key, value)
26059        self.write_keyword("OBJECT_AGG");
26060        self.write("(");
26061        self.generate_expression(&e.this)?;
26062        self.write(", ");
26063        self.generate_expression(&e.expression)?;
26064        self.write(")");
26065        Ok(())
26066    }
26067
26068    fn generate_object_identifier(&mut self, e: &ObjectIdentifier) -> Result<()> {
26069        // Python: Just returns the name
26070        self.generate_expression(&e.this)?;
26071        Ok(())
26072    }
26073
26074    fn generate_object_insert(&mut self, e: &ObjectInsert) -> Result<()> {
26075        // OBJECT_INSERT(obj, key, value, [update_flag])
26076        self.write_keyword("OBJECT_INSERT");
26077        self.write("(");
26078        self.generate_expression(&e.this)?;
26079        if let Some(key) = &e.key {
26080            self.write(", ");
26081            self.generate_expression(key)?;
26082        }
26083        if let Some(value) = &e.value {
26084            self.write(", ");
26085            self.generate_expression(value)?;
26086        }
26087        if let Some(update_flag) = &e.update_flag {
26088            self.write(", ");
26089            self.generate_expression(update_flag)?;
26090        }
26091        self.write(")");
26092        Ok(())
26093    }
26094
26095    fn generate_offset(&mut self, e: &Offset) -> Result<()> {
26096        // OFFSET value [ROW|ROWS]
26097        self.write_keyword("OFFSET");
26098        self.write_space();
26099        self.generate_expression(&e.this)?;
26100        // Output ROWS keyword only for TSQL/Oracle targets
26101        if e.rows == Some(true) && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Oracle)) {
26102            self.write_space();
26103            self.write_keyword("ROWS");
26104        }
26105        Ok(())
26106    }
26107
26108    fn generate_qualify(&mut self, e: &Qualify) -> Result<()> {
26109        // QUALIFY condition (Snowflake/BigQuery)
26110        self.write_keyword("QUALIFY");
26111        self.write_space();
26112        self.generate_expression(&e.this)?;
26113        Ok(())
26114    }
26115
26116    fn generate_on_cluster(&mut self, e: &OnCluster) -> Result<()> {
26117        // ON CLUSTER cluster_name
26118        self.write_keyword("ON CLUSTER");
26119        self.write_space();
26120        self.generate_expression(&e.this)?;
26121        Ok(())
26122    }
26123
26124    fn generate_on_commit_property(&mut self, e: &OnCommitProperty) -> Result<()> {
26125        // ON COMMIT [DELETE ROWS | PRESERVE ROWS]
26126        self.write_keyword("ON COMMIT");
26127        if e.delete.is_some() {
26128            self.write_keyword(" DELETE ROWS");
26129        } else {
26130            self.write_keyword(" PRESERVE ROWS");
26131        }
26132        Ok(())
26133    }
26134
26135    fn generate_on_condition(&mut self, e: &OnCondition) -> Result<()> {
26136        // Python: error/empty/null handling
26137        if let Some(empty) = &e.empty {
26138            self.generate_expression(empty)?;
26139            self.write_keyword(" ON EMPTY");
26140        }
26141        if let Some(error) = &e.error {
26142            if e.empty.is_some() {
26143                self.write_space();
26144            }
26145            self.generate_expression(error)?;
26146            self.write_keyword(" ON ERROR");
26147        }
26148        if let Some(null) = &e.null {
26149            if e.empty.is_some() || e.error.is_some() {
26150                self.write_space();
26151            }
26152            self.generate_expression(null)?;
26153            self.write_keyword(" ON NULL");
26154        }
26155        Ok(())
26156    }
26157
26158    fn generate_on_conflict(&mut self, e: &OnConflict) -> Result<()> {
26159        // Materialize doesn't support ON CONFLICT - skip entirely
26160        if matches!(self.config.dialect, Some(DialectType::Materialize)) {
26161            return Ok(());
26162        }
26163        // Python: ON CONFLICT|ON DUPLICATE KEY [ON CONSTRAINT constraint] [conflict_keys] action
26164        if e.duplicate.is_some() {
26165            // MySQL: ON DUPLICATE KEY UPDATE col = val, ...
26166            self.write_keyword("ON DUPLICATE KEY UPDATE");
26167            for (i, expr) in e.expressions.iter().enumerate() {
26168                if i > 0 {
26169                    self.write(",");
26170                }
26171                self.write_space();
26172                self.generate_expression(expr)?;
26173            }
26174            return Ok(());
26175        } else {
26176            self.write_keyword("ON CONFLICT");
26177        }
26178        if let Some(constraint) = &e.constraint {
26179            self.write_keyword(" ON CONSTRAINT ");
26180            self.generate_expression(constraint)?;
26181        }
26182        if let Some(conflict_keys) = &e.conflict_keys {
26183            // conflict_keys can be a Tuple containing expressions
26184            if let Expression::Tuple(t) = conflict_keys.as_ref() {
26185                self.write("(");
26186                for (i, expr) in t.expressions.iter().enumerate() {
26187                    if i > 0 {
26188                        self.write(", ");
26189                    }
26190                    self.generate_expression(expr)?;
26191                }
26192                self.write(")");
26193            } else {
26194                self.write("(");
26195                self.generate_expression(conflict_keys)?;
26196                self.write(")");
26197            }
26198        }
26199        if let Some(index_predicate) = &e.index_predicate {
26200            self.write_keyword(" WHERE ");
26201            self.generate_expression(index_predicate)?;
26202        }
26203        if let Some(action) = &e.action {
26204            // Check if action is "NOTHING" or an UPDATE set
26205            if let Expression::Identifier(id) = action.as_ref() {
26206                if id.name == "NOTHING" || id.name.to_uppercase() == "NOTHING" {
26207                    self.write_keyword(" DO NOTHING");
26208                } else {
26209                    self.write_keyword(" DO ");
26210                    self.generate_expression(action)?;
26211                }
26212            } else if let Expression::Tuple(t) = action.as_ref() {
26213                // DO UPDATE SET col1 = val1, col2 = val2
26214                self.write_keyword(" DO UPDATE SET ");
26215                for (i, expr) in t.expressions.iter().enumerate() {
26216                    if i > 0 {
26217                        self.write(", ");
26218                    }
26219                    self.generate_expression(expr)?;
26220                }
26221            } else {
26222                self.write_keyword(" DO ");
26223                self.generate_expression(action)?;
26224            }
26225        }
26226        // WHERE clause for the UPDATE action
26227        if let Some(where_) = &e.where_ {
26228            self.write_keyword(" WHERE ");
26229            self.generate_expression(where_)?;
26230        }
26231        Ok(())
26232    }
26233
26234    fn generate_on_property(&mut self, e: &OnProperty) -> Result<()> {
26235        // ON property_value
26236        self.write_keyword("ON");
26237        self.write_space();
26238        self.generate_expression(&e.this)?;
26239        Ok(())
26240    }
26241
26242    fn generate_opclass(&mut self, e: &Opclass) -> Result<()> {
26243        // Python: this expression (e.g., column opclass)
26244        self.generate_expression(&e.this)?;
26245        self.write_space();
26246        self.generate_expression(&e.expression)?;
26247        Ok(())
26248    }
26249
26250    fn generate_open_json(&mut self, e: &OpenJSON) -> Result<()> {
26251        // Python: OPENJSON(this[, path]) [WITH (columns)]
26252        self.write_keyword("OPENJSON");
26253        self.write("(");
26254        self.generate_expression(&e.this)?;
26255        if let Some(path) = &e.path {
26256            self.write(", ");
26257            self.generate_expression(path)?;
26258        }
26259        self.write(")");
26260        if !e.expressions.is_empty() {
26261            self.write_keyword(" WITH");
26262            if self.config.pretty {
26263                self.write(" (\n");
26264                self.indent_level += 2;
26265                for (i, expr) in e.expressions.iter().enumerate() {
26266                    if i > 0 {
26267                        self.write(",\n");
26268                    }
26269                    self.write_indent();
26270                    self.generate_expression(expr)?;
26271                }
26272                self.write("\n");
26273                self.indent_level -= 2;
26274                self.write(")");
26275            } else {
26276                self.write(" (");
26277                for (i, expr) in e.expressions.iter().enumerate() {
26278                    if i > 0 {
26279                        self.write(", ");
26280                    }
26281                    self.generate_expression(expr)?;
26282                }
26283                self.write(")");
26284            }
26285        }
26286        Ok(())
26287    }
26288
26289    fn generate_open_json_column_def(&mut self, e: &OpenJSONColumnDef) -> Result<()> {
26290        // Python: this kind [path] [AS JSON]
26291        self.generate_expression(&e.this)?;
26292        self.write_space();
26293        // Use parsed data_type if available, otherwise fall back to kind string
26294        if let Some(ref dt) = e.data_type {
26295            self.generate_data_type(dt)?;
26296        } else if !e.kind.is_empty() {
26297            self.write(&e.kind);
26298        }
26299        if let Some(path) = &e.path {
26300            self.write_space();
26301            self.generate_expression(path)?;
26302        }
26303        if e.as_json.is_some() {
26304            self.write_keyword(" AS JSON");
26305        }
26306        Ok(())
26307    }
26308
26309    fn generate_operator(&mut self, e: &Operator) -> Result<()> {
26310        // this OPERATOR(op) expression
26311        self.generate_expression(&e.this)?;
26312        self.write_space();
26313        if let Some(op) = &e.operator {
26314            self.write_keyword("OPERATOR");
26315            self.write("(");
26316            self.generate_expression(op)?;
26317            self.write(")");
26318        }
26319        self.write_space();
26320        self.generate_expression(&e.expression)?;
26321        Ok(())
26322    }
26323
26324    fn generate_order_by(&mut self, e: &OrderBy) -> Result<()> {
26325        // ORDER BY expr1 [ASC|DESC] [NULLS FIRST|LAST], expr2 ...
26326        self.write_keyword("ORDER BY");
26327        let pretty_clickhouse_single_paren = self.config.pretty
26328            && matches!(self.config.dialect, Some(DialectType::ClickHouse))
26329            && e.expressions.len() == 1
26330            && matches!(e.expressions[0].this, Expression::Paren(ref p) if !matches!(p.this, Expression::Tuple(_)));
26331        let clickhouse_single_tuple = matches!(self.config.dialect, Some(DialectType::ClickHouse))
26332            && e.expressions.len() == 1
26333            && matches!(e.expressions[0].this, Expression::Tuple(_))
26334            && !e.expressions[0].desc
26335            && e.expressions[0].nulls_first.is_none();
26336
26337        if pretty_clickhouse_single_paren {
26338            self.write_space();
26339            if let Expression::Paren(p) = &e.expressions[0].this {
26340                self.write("(");
26341                self.write_newline();
26342                self.indent_level += 1;
26343                self.write_indent();
26344                self.generate_expression(&p.this)?;
26345                self.indent_level -= 1;
26346                self.write_newline();
26347                self.write(")");
26348            }
26349            return Ok(());
26350        }
26351
26352        if clickhouse_single_tuple {
26353            self.write_space();
26354            if let Expression::Tuple(t) = &e.expressions[0].this {
26355                self.write("(");
26356                for (i, expr) in t.expressions.iter().enumerate() {
26357                    if i > 0 {
26358                        self.write(", ");
26359                    }
26360                    self.generate_expression(expr)?;
26361                }
26362                self.write(")");
26363            }
26364            return Ok(());
26365        }
26366
26367        self.write_space();
26368        for (i, ordered) in e.expressions.iter().enumerate() {
26369            if i > 0 {
26370                self.write(", ");
26371            }
26372            self.generate_expression(&ordered.this)?;
26373            if ordered.desc {
26374                self.write_space();
26375                self.write_keyword("DESC");
26376            } else if ordered.explicit_asc {
26377                self.write_space();
26378                self.write_keyword("ASC");
26379            }
26380            if let Some(nulls_first) = ordered.nulls_first {
26381                // In Dremio, NULLS LAST is the default, so skip generating it
26382                let skip_nulls_last = !nulls_first
26383                    && matches!(self.config.dialect, Some(DialectType::Dremio));
26384                if !skip_nulls_last {
26385                    self.write_space();
26386                    self.write_keyword("NULLS");
26387                    self.write_space();
26388                    if nulls_first {
26389                        self.write_keyword("FIRST");
26390                    } else {
26391                        self.write_keyword("LAST");
26392                    }
26393                }
26394            }
26395        }
26396        Ok(())
26397    }
26398
26399    fn generate_output_model_property(&mut self, e: &OutputModelProperty) -> Result<()> {
26400        // OUTPUT(model)
26401        self.write_keyword("OUTPUT");
26402        self.write("(");
26403        if self.config.pretty {
26404            self.indent_level += 1;
26405            self.write_newline();
26406            self.write_indent();
26407            self.generate_expression(&e.this)?;
26408            self.indent_level -= 1;
26409            self.write_newline();
26410        } else {
26411            self.generate_expression(&e.this)?;
26412        }
26413        self.write(")");
26414        Ok(())
26415    }
26416
26417    fn generate_overflow_truncate_behavior(&mut self, e: &OverflowTruncateBehavior) -> Result<()> {
26418        // Python: TRUNCATE [filler] WITH|WITHOUT COUNT
26419        self.write_keyword("TRUNCATE");
26420        if let Some(this) = &e.this {
26421            self.write_space();
26422            self.generate_expression(this)?;
26423        }
26424        if e.with_count.is_some() {
26425            self.write_keyword(" WITH COUNT");
26426        } else {
26427            self.write_keyword(" WITHOUT COUNT");
26428        }
26429        Ok(())
26430    }
26431
26432    fn generate_parameterized_agg(&mut self, e: &ParameterizedAgg) -> Result<()> {
26433        // Python: name(expressions)(params)
26434        self.generate_expression(&e.this)?;
26435        self.write("(");
26436        for (i, expr) in e.expressions.iter().enumerate() {
26437            if i > 0 {
26438                self.write(", ");
26439            }
26440            self.generate_expression(expr)?;
26441        }
26442        self.write(")(");
26443        for (i, param) in e.params.iter().enumerate() {
26444            if i > 0 {
26445                self.write(", ");
26446            }
26447            self.generate_expression(param)?;
26448        }
26449        self.write(")");
26450        Ok(())
26451    }
26452
26453    fn generate_parse_datetime(&mut self, e: &ParseDatetime) -> Result<()> {
26454        // PARSE_DATETIME(format, this) or similar
26455        self.write_keyword("PARSE_DATETIME");
26456        self.write("(");
26457        if let Some(format) = &e.format {
26458            self.write("'");
26459            self.write(format);
26460            self.write("', ");
26461        }
26462        self.generate_expression(&e.this)?;
26463        if let Some(zone) = &e.zone {
26464            self.write(", ");
26465            self.generate_expression(zone)?;
26466        }
26467        self.write(")");
26468        Ok(())
26469    }
26470
26471    fn generate_parse_ip(&mut self, e: &ParseIp) -> Result<()> {
26472        // PARSE_IP(this, type, permissive)
26473        self.write_keyword("PARSE_IP");
26474        self.write("(");
26475        self.generate_expression(&e.this)?;
26476        if let Some(type_) = &e.type_ {
26477            self.write(", ");
26478            self.generate_expression(type_)?;
26479        }
26480        if let Some(permissive) = &e.permissive {
26481            self.write(", ");
26482            self.generate_expression(permissive)?;
26483        }
26484        self.write(")");
26485        Ok(())
26486    }
26487
26488    fn generate_parse_json(&mut self, e: &ParseJSON) -> Result<()> {
26489        // PARSE_JSON(this, [expression])
26490        self.write_keyword("PARSE_JSON");
26491        self.write("(");
26492        self.generate_expression(&e.this)?;
26493        if let Some(expression) = &e.expression {
26494            self.write(", ");
26495            self.generate_expression(expression)?;
26496        }
26497        self.write(")");
26498        Ok(())
26499    }
26500
26501    fn generate_parse_time(&mut self, e: &ParseTime) -> Result<()> {
26502        // PARSE_TIME(format, this) or STR_TO_TIME(this, format)
26503        self.write_keyword("PARSE_TIME");
26504        self.write("(");
26505        self.write(&format!("'{}'", e.format));
26506        self.write(", ");
26507        self.generate_expression(&e.this)?;
26508        self.write(")");
26509        Ok(())
26510    }
26511
26512    fn generate_parse_url(&mut self, e: &ParseUrl) -> Result<()> {
26513        // PARSE_URL(this, [part_to_extract], [key], [permissive])
26514        self.write_keyword("PARSE_URL");
26515        self.write("(");
26516        self.generate_expression(&e.this)?;
26517        if let Some(part) = &e.part_to_extract {
26518            self.write(", ");
26519            self.generate_expression(part)?;
26520        }
26521        if let Some(key) = &e.key {
26522            self.write(", ");
26523            self.generate_expression(key)?;
26524        }
26525        if let Some(permissive) = &e.permissive {
26526            self.write(", ");
26527            self.generate_expression(permissive)?;
26528        }
26529        self.write(")");
26530        Ok(())
26531    }
26532
26533    fn generate_partition_expr(&mut self, e: &Partition) -> Result<()> {
26534        // PARTITION(expr1, expr2, ...) or SUBPARTITION(expr1, expr2, ...)
26535        if e.subpartition {
26536            self.write_keyword("SUBPARTITION");
26537        } else {
26538            self.write_keyword("PARTITION");
26539        }
26540        self.write("(");
26541        for (i, expr) in e.expressions.iter().enumerate() {
26542            if i > 0 {
26543                self.write(", ");
26544            }
26545            self.generate_expression(expr)?;
26546        }
26547        self.write(")");
26548        Ok(())
26549    }
26550
26551    fn generate_partition_bound_spec(&mut self, e: &PartitionBoundSpec) -> Result<()> {
26552        // IN (values) or WITH (MODULUS this, REMAINDER expression) or FROM (from) TO (to)
26553        if let Some(this) = &e.this {
26554            if let Some(expression) = &e.expression {
26555                // WITH (MODULUS this, REMAINDER expression)
26556                self.write_keyword("WITH");
26557                self.write(" (");
26558                self.write_keyword("MODULUS");
26559                self.write_space();
26560                self.generate_expression(this)?;
26561                self.write(", ");
26562                self.write_keyword("REMAINDER");
26563                self.write_space();
26564                self.generate_expression(expression)?;
26565                self.write(")");
26566            } else {
26567                // IN (this) - this could be a list
26568                self.write_keyword("IN");
26569                self.write(" (");
26570                self.generate_partition_bound_values(this)?;
26571                self.write(")");
26572            }
26573        } else if let (Some(from), Some(to)) = (&e.from_expressions, &e.to_expressions) {
26574            // FROM (from_expressions) TO (to_expressions)
26575            self.write_keyword("FROM");
26576            self.write(" (");
26577            self.generate_partition_bound_values(from)?;
26578            self.write(") ");
26579            self.write_keyword("TO");
26580            self.write(" (");
26581            self.generate_partition_bound_values(to)?;
26582            self.write(")");
26583        }
26584        Ok(())
26585    }
26586
26587    /// Generate partition bound values - handles Tuple expressions by outputting
26588    /// contents without wrapping parens (since caller provides the parens)
26589    fn generate_partition_bound_values(&mut self, expr: &Expression) -> Result<()> {
26590        if let Expression::Tuple(t) = expr {
26591            for (i, e) in t.expressions.iter().enumerate() {
26592                if i > 0 {
26593                    self.write(", ");
26594                }
26595                self.generate_expression(e)?;
26596            }
26597            Ok(())
26598        } else {
26599            self.generate_expression(expr)
26600        }
26601    }
26602
26603    fn generate_partition_by_list_property(&mut self, e: &PartitionByListProperty) -> Result<()> {
26604        // PARTITION BY LIST (partition_expressions) (create_expressions)
26605        self.write_keyword("PARTITION BY LIST");
26606        if let Some(partition_exprs) = &e.partition_expressions {
26607            self.write(" (");
26608            // Unwrap Tuple for partition columns (don't generate outer parens from Tuple)
26609            self.generate_doris_partition_expressions(partition_exprs)?;
26610            self.write(")");
26611        }
26612        if let Some(create_exprs) = &e.create_expressions {
26613            self.write(" (");
26614            // Unwrap Tuple for partition definitions
26615            self.generate_doris_partition_definitions(create_exprs)?;
26616            self.write(")");
26617        }
26618        Ok(())
26619    }
26620
26621    fn generate_partition_by_range_property(&mut self, e: &PartitionByRangeProperty) -> Result<()> {
26622        // PARTITION BY RANGE (partition_expressions) (create_expressions)
26623        self.write_keyword("PARTITION BY RANGE");
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            // Check for dynamic partition (PartitionByRangePropertyDynamic) or static (Tuple of Partition)
26633            self.generate_doris_partition_definitions(create_exprs)?;
26634            self.write(")");
26635        }
26636        Ok(())
26637    }
26638
26639    /// Generate Doris partition column expressions (unwrap Tuple)
26640    fn generate_doris_partition_expressions(&mut self, expr: &Expression) -> Result<()> {
26641        if let Expression::Tuple(t) = expr {
26642            for (i, e) in t.expressions.iter().enumerate() {
26643                if i > 0 {
26644                    self.write(", ");
26645                }
26646                self.generate_expression(e)?;
26647            }
26648        } else {
26649            self.generate_expression(expr)?;
26650        }
26651        Ok(())
26652    }
26653
26654    /// Generate Doris partition definitions (comma-separated Partition expressions)
26655    fn generate_doris_partition_definitions(&mut self, expr: &Expression) -> Result<()> {
26656        match expr {
26657            Expression::Tuple(t) => {
26658                // Multiple partitions, comma-separated
26659                for (i, part) in t.expressions.iter().enumerate() {
26660                    if i > 0 {
26661                        self.write(", ");
26662                    }
26663                    // For Partition expressions, generate the inner PartitionRange/PartitionList directly
26664                    if let Expression::Partition(p) = part {
26665                        for (j, inner) in p.expressions.iter().enumerate() {
26666                            if j > 0 {
26667                                self.write(", ");
26668                            }
26669                            self.generate_expression(inner)?;
26670                        }
26671                    } else {
26672                        self.generate_expression(part)?;
26673                    }
26674                }
26675            }
26676            Expression::PartitionByRangePropertyDynamic(_) => {
26677                // Dynamic partition - FROM/TO/INTERVAL
26678                self.generate_expression(expr)?;
26679            }
26680            _ => {
26681                self.generate_expression(expr)?;
26682            }
26683        }
26684        Ok(())
26685    }
26686
26687    fn generate_partition_by_range_property_dynamic(&mut self, e: &PartitionByRangePropertyDynamic) -> Result<()> {
26688        // Doris: FROM (start) TO (end) INTERVAL n UNIT
26689        if let Some(start) = &e.start {
26690            self.write_keyword("FROM");
26691            self.write(" (");
26692            self.generate_expression(start)?;
26693            self.write(")");
26694        }
26695        if let Some(end) = &e.end {
26696            self.write_space();
26697            self.write_keyword("TO");
26698            self.write(" (");
26699            self.generate_expression(end)?;
26700            self.write(")");
26701        }
26702        if let Some(every) = &e.every {
26703            self.write_space();
26704            // Generate INTERVAL n UNIT (not quoted, for Doris dynamic partition)
26705            self.generate_doris_interval(every)?;
26706        }
26707        Ok(())
26708    }
26709
26710    /// Generate Doris-style interval without quoting numbers: INTERVAL n UNIT
26711    fn generate_doris_interval(&mut self, expr: &Expression) -> Result<()> {
26712        if let Expression::Interval(interval) = expr {
26713            self.write_keyword("INTERVAL");
26714            if let Some(ref value) = interval.this {
26715                self.write_space();
26716                // Don't quote numbers for Doris partition interval
26717                self.generate_expression(value)?;
26718            }
26719            if let Some(ref unit_spec) = interval.unit {
26720                self.write_space();
26721                self.write_interval_unit_spec(unit_spec)?;
26722            }
26723            Ok(())
26724        } else {
26725            self.generate_expression(expr)
26726        }
26727    }
26728
26729    fn generate_partition_by_truncate(&mut self, e: &PartitionByTruncate) -> Result<()> {
26730        // TRUNCATE(expression, this)
26731        self.write_keyword("TRUNCATE");
26732        self.write("(");
26733        self.generate_expression(&e.expression)?;
26734        self.write(", ");
26735        self.generate_expression(&e.this)?;
26736        self.write(")");
26737        Ok(())
26738    }
26739
26740    fn generate_partition_list(&mut self, e: &PartitionList) -> Result<()> {
26741        // Doris: PARTITION name VALUES IN (val1, val2)
26742        self.write_keyword("PARTITION");
26743        self.write_space();
26744        self.generate_expression(&e.this)?;
26745        self.write_space();
26746        self.write_keyword("VALUES IN");
26747        self.write(" (");
26748        for (i, expr) in e.expressions.iter().enumerate() {
26749            if i > 0 {
26750                self.write(", ");
26751            }
26752            self.generate_expression(expr)?;
26753        }
26754        self.write(")");
26755        Ok(())
26756    }
26757
26758    fn generate_partition_range(&mut self, e: &PartitionRange) -> Result<()> {
26759        // Check if this is a TSQL-style simple range (e.g., "2 TO 5")
26760        // TSQL ranges have no expressions and just use `this TO expression`
26761        if e.expressions.is_empty() && e.expression.is_some() {
26762            // TSQL: simple range like "2 TO 5" - no PARTITION keyword
26763            self.generate_expression(&e.this)?;
26764            self.write_space();
26765            self.write_keyword("TO");
26766            self.write_space();
26767            self.generate_expression(e.expression.as_ref().unwrap())?;
26768            return Ok(());
26769        }
26770
26771        // Doris: PARTITION name VALUES LESS THAN (val) or PARTITION name VALUES [(val1), (val2))
26772        self.write_keyword("PARTITION");
26773        self.write_space();
26774        self.generate_expression(&e.this)?;
26775        self.write_space();
26776
26777        // Check if expressions contain Tuple (bracket notation) or single values (LESS THAN)
26778        if e.expressions.len() == 1 {
26779            // Single value: VALUES LESS THAN (val)
26780            self.write_keyword("VALUES LESS THAN");
26781            self.write(" (");
26782            self.generate_expression(&e.expressions[0])?;
26783            self.write(")");
26784        } else if !e.expressions.is_empty() {
26785            // Multiple values with Tuple: VALUES [(val1), (val2))
26786            self.write_keyword("VALUES");
26787            self.write(" [");
26788            for (i, expr) in e.expressions.iter().enumerate() {
26789                if i > 0 {
26790                    self.write(", ");
26791                }
26792                // If the expr is a Tuple, generate its contents wrapped in parens
26793                if let Expression::Tuple(t) = expr {
26794                    self.write("(");
26795                    for (j, inner) in t.expressions.iter().enumerate() {
26796                        if j > 0 {
26797                            self.write(", ");
26798                        }
26799                        self.generate_expression(inner)?;
26800                    }
26801                    self.write(")");
26802                } else {
26803                    self.write("(");
26804                    self.generate_expression(expr)?;
26805                    self.write(")");
26806                }
26807            }
26808            self.write(")");
26809        }
26810        Ok(())
26811    }
26812
26813    fn generate_partitioned_by_bucket(&mut self, e: &PartitionedByBucket) -> Result<()> {
26814        // BUCKET(this, expression)
26815        self.write_keyword("BUCKET");
26816        self.write("(");
26817        self.generate_expression(&e.this)?;
26818        self.write(", ");
26819        self.generate_expression(&e.expression)?;
26820        self.write(")");
26821        Ok(())
26822    }
26823
26824    fn generate_partitioned_by_property(&mut self, e: &PartitionedByProperty) -> Result<()> {
26825        // PARTITIONED BY this (Teradata/ClickHouse use PARTITION BY)
26826        if matches!(
26827            self.config.dialect,
26828            Some(crate::dialects::DialectType::Teradata) | Some(crate::dialects::DialectType::ClickHouse)
26829        ) {
26830            self.write_keyword("PARTITION BY");
26831        } else {
26832            self.write_keyword("PARTITIONED BY");
26833        }
26834        self.write_space();
26835        // In pretty mode, always use multiline tuple format for PARTITIONED BY
26836        if self.config.pretty {
26837            if let Expression::Tuple(ref tuple) = *e.this {
26838                self.write("(");
26839                self.write_newline();
26840                self.indent_level += 1;
26841                for (i, expr) in tuple.expressions.iter().enumerate() {
26842                    if i > 0 {
26843                        self.write(",");
26844                        self.write_newline();
26845                    }
26846                    self.write_indent();
26847                    self.generate_expression(expr)?;
26848                }
26849                self.indent_level -= 1;
26850                self.write_newline();
26851                self.write(")");
26852            } else {
26853                self.generate_expression(&e.this)?;
26854            }
26855        } else {
26856            self.generate_expression(&e.this)?;
26857        }
26858        Ok(())
26859    }
26860
26861    fn generate_partitioned_of_property(&mut self, e: &PartitionedOfProperty) -> Result<()> {
26862        // PARTITION OF this FOR VALUES expression or PARTITION OF this DEFAULT
26863        self.write_keyword("PARTITION OF");
26864        self.write_space();
26865        self.generate_expression(&e.this)?;
26866        // Check if expression is a PartitionBoundSpec
26867        if let Expression::PartitionBoundSpec(_) = e.expression.as_ref() {
26868            self.write_space();
26869            self.write_keyword("FOR VALUES");
26870            self.write_space();
26871            self.generate_expression(&e.expression)?;
26872        } else {
26873            self.write_space();
26874            self.write_keyword("DEFAULT");
26875        }
26876        Ok(())
26877    }
26878
26879    fn generate_period_for_system_time_constraint(&mut self, e: &PeriodForSystemTimeConstraint) -> Result<()> {
26880        // PERIOD FOR SYSTEM_TIME (this, expression)
26881        self.write_keyword("PERIOD FOR SYSTEM_TIME");
26882        self.write(" (");
26883        self.generate_expression(&e.this)?;
26884        self.write(", ");
26885        self.generate_expression(&e.expression)?;
26886        self.write(")");
26887        Ok(())
26888    }
26889
26890    fn generate_pivot_alias(&mut self, e: &PivotAlias) -> Result<()> {
26891        // value AS alias
26892        // The alias can be an identifier or an expression (e.g., string concatenation)
26893        self.generate_expression(&e.this)?;
26894        self.write_space();
26895        self.write_keyword("AS");
26896        self.write_space();
26897        // When target dialect uses identifiers for UNPIVOT aliases, convert literals to identifiers
26898        if self.config.unpivot_aliases_are_identifiers {
26899            match &e.alias {
26900                Expression::Literal(Literal::String(s)) => {
26901                    // Convert string literal to identifier
26902                    self.generate_identifier(&Identifier::new(s.clone()))?;
26903                }
26904                Expression::Literal(Literal::Number(n)) => {
26905                    // Convert number literal to quoted identifier
26906                    let mut id = Identifier::new(n.clone());
26907                    id.quoted = true;
26908                    self.generate_identifier(&id)?;
26909                }
26910                other => {
26911                    self.generate_expression(other)?;
26912                }
26913            }
26914        } else {
26915            self.generate_expression(&e.alias)?;
26916        }
26917        Ok(())
26918    }
26919
26920    fn generate_pivot_any(&mut self, e: &PivotAny) -> Result<()> {
26921        // ANY or ANY [expression]
26922        self.write_keyword("ANY");
26923        if let Some(this) = &e.this {
26924            self.write_space();
26925            self.generate_expression(this)?;
26926        }
26927        Ok(())
26928    }
26929
26930    fn generate_predict(&mut self, e: &Predict) -> Result<()> {
26931        // ML.PREDICT(MODEL this, expression, [params_struct])
26932        self.write_keyword("ML.PREDICT");
26933        self.write("(");
26934        self.write_keyword("MODEL");
26935        self.write_space();
26936        self.generate_expression(&e.this)?;
26937        self.write(", ");
26938        self.generate_expression(&e.expression)?;
26939        if let Some(params) = &e.params_struct {
26940            self.write(", ");
26941            self.generate_expression(params)?;
26942        }
26943        self.write(")");
26944        Ok(())
26945    }
26946
26947    fn generate_previous_day(&mut self, e: &PreviousDay) -> Result<()> {
26948        // PREVIOUS_DAY(this, expression)
26949        self.write_keyword("PREVIOUS_DAY");
26950        self.write("(");
26951        self.generate_expression(&e.this)?;
26952        self.write(", ");
26953        self.generate_expression(&e.expression)?;
26954        self.write(")");
26955        Ok(())
26956    }
26957
26958    fn generate_primary_key(&mut self, e: &PrimaryKey) -> Result<()> {
26959        // PRIMARY KEY [name] (columns) [INCLUDE (...)] [options]
26960        self.write_keyword("PRIMARY KEY");
26961        if let Some(name) = &e.this {
26962            self.write_space();
26963            self.generate_expression(name)?;
26964        }
26965        if !e.expressions.is_empty() {
26966            self.write(" (");
26967            for (i, expr) in e.expressions.iter().enumerate() {
26968                if i > 0 {
26969                    self.write(", ");
26970                }
26971                self.generate_expression(expr)?;
26972            }
26973            self.write(")");
26974        }
26975        if let Some(include) = &e.include {
26976            self.write_space();
26977            self.generate_expression(include)?;
26978        }
26979        if !e.options.is_empty() {
26980            self.write_space();
26981            for (i, opt) in e.options.iter().enumerate() {
26982                if i > 0 {
26983                    self.write_space();
26984                }
26985                self.generate_expression(opt)?;
26986            }
26987        }
26988        Ok(())
26989    }
26990
26991    fn generate_primary_key_column_constraint(&mut self, _e: &PrimaryKeyColumnConstraint) -> Result<()> {
26992        // PRIMARY KEY constraint at column level
26993        self.write_keyword("PRIMARY KEY");
26994        Ok(())
26995    }
26996
26997    fn generate_path_column_constraint(&mut self, e: &PathColumnConstraint) -> Result<()> {
26998        // PATH 'xpath' constraint for XMLTABLE/JSON_TABLE columns
26999        self.write_keyword("PATH");
27000        self.write_space();
27001        self.generate_expression(&e.this)?;
27002        Ok(())
27003    }
27004
27005    fn generate_projection_def(&mut self, e: &ProjectionDef) -> Result<()> {
27006        // PROJECTION this (expression)
27007        self.write_keyword("PROJECTION");
27008        self.write_space();
27009        self.generate_expression(&e.this)?;
27010        self.write(" (");
27011        self.generate_expression(&e.expression)?;
27012        self.write(")");
27013        Ok(())
27014    }
27015
27016    fn generate_properties(&mut self, e: &Properties) -> Result<()> {
27017        // Properties list
27018        for (i, prop) in e.expressions.iter().enumerate() {
27019            if i > 0 {
27020                self.write(", ");
27021            }
27022            self.generate_expression(prop)?;
27023        }
27024        Ok(())
27025    }
27026
27027    fn generate_property(&mut self, e: &Property) -> Result<()> {
27028        // name=value
27029        self.generate_expression(&e.this)?;
27030        if let Some(value) = &e.value {
27031            self.write("=");
27032            self.generate_expression(value)?;
27033        }
27034        Ok(())
27035    }
27036
27037    /// Generate BigQuery-style OPTIONS clause: OPTIONS (key=value, key=value, ...)
27038    fn generate_options_clause(&mut self, options: &[Expression]) -> Result<()> {
27039        self.write_keyword("OPTIONS");
27040        self.write(" (");
27041        for (i, opt) in options.iter().enumerate() {
27042            if i > 0 {
27043                self.write(", ");
27044            }
27045            self.generate_option_expression(opt)?;
27046        }
27047        self.write(")");
27048        Ok(())
27049    }
27050
27051    /// Generate Doris/StarRocks-style PROPERTIES clause: PROPERTIES ('key'='value', 'key'='value', ...)
27052    fn generate_properties_clause(&mut self, properties: &[Expression]) -> Result<()> {
27053        self.write_keyword("PROPERTIES");
27054        self.write(" (");
27055        for (i, prop) in properties.iter().enumerate() {
27056            if i > 0 {
27057                self.write(", ");
27058            }
27059            self.generate_option_expression(prop)?;
27060        }
27061        self.write(")");
27062        Ok(())
27063    }
27064
27065    /// Generate Databricks-style ENVIRONMENT clause: ENVIRONMENT (key = 'value', key = 'value', ...)
27066    fn generate_environment_clause(&mut self, environment: &[Expression]) -> Result<()> {
27067        self.write_keyword("ENVIRONMENT");
27068        self.write(" (");
27069        for (i, env_item) in environment.iter().enumerate() {
27070            if i > 0 {
27071                self.write(", ");
27072            }
27073            self.generate_environment_expression(env_item)?;
27074        }
27075        self.write(")");
27076        Ok(())
27077    }
27078
27079    /// Generate an environment expression with spaces around =
27080    fn generate_environment_expression(&mut self, expr: &Expression) -> Result<()> {
27081        match expr {
27082            Expression::Eq(eq) => {
27083                // Generate key = value with spaces (Databricks ENVIRONMENT style)
27084                self.generate_expression(&eq.left)?;
27085                self.write(" = ");
27086                self.generate_expression(&eq.right)?;
27087                Ok(())
27088            }
27089            _ => self.generate_expression(expr),
27090        }
27091    }
27092
27093    /// Generate Hive-style TBLPROPERTIES clause: TBLPROPERTIES ('key'='value', ...)
27094    fn generate_tblproperties_clause(&mut self, options: &[Expression]) -> Result<()> {
27095        self.write_keyword("TBLPROPERTIES");
27096        if self.config.pretty {
27097            self.write(" (");
27098            self.write_newline();
27099            self.indent_level += 1;
27100            for (i, opt) in options.iter().enumerate() {
27101                if i > 0 {
27102                    self.write(",");
27103                    self.write_newline();
27104                }
27105                self.write_indent();
27106                self.generate_option_expression(opt)?;
27107            }
27108            self.indent_level -= 1;
27109            self.write_newline();
27110            self.write(")");
27111        } else {
27112            self.write(" (");
27113            for (i, opt) in options.iter().enumerate() {
27114                if i > 0 {
27115                    self.write(", ");
27116                }
27117                self.generate_option_expression(opt)?;
27118            }
27119            self.write(")");
27120        }
27121        Ok(())
27122    }
27123
27124    /// Generate an option expression without spaces around =
27125    fn generate_option_expression(&mut self, expr: &Expression) -> Result<()> {
27126        match expr {
27127            Expression::Eq(eq) => {
27128                // Generate key=value without spaces
27129                self.generate_expression(&eq.left)?;
27130                self.write("=");
27131                self.generate_expression(&eq.right)?;
27132                Ok(())
27133            }
27134            _ => self.generate_expression(expr),
27135        }
27136    }
27137
27138    fn generate_pseudo_type(&mut self, e: &PseudoType) -> Result<()> {
27139        // Just output the name
27140        self.generate_expression(&e.this)?;
27141        Ok(())
27142    }
27143
27144    fn generate_put(&mut self, e: &PutStmt) -> Result<()> {
27145        // PUT source_file @stage [options]
27146        self.write_keyword("PUT");
27147        self.write_space();
27148
27149        // Source file path - preserve original quoting
27150        if e.source_quoted {
27151            self.write("'");
27152            self.write(&e.source);
27153            self.write("'");
27154        } else {
27155            self.write(&e.source);
27156        }
27157
27158        self.write_space();
27159
27160        // Target stage reference - output the string directly (includes @)
27161        if let Expression::Literal(Literal::String(s)) = &e.target {
27162            self.write(s);
27163        } else {
27164            self.generate_expression(&e.target)?;
27165        }
27166
27167        // Optional parameters: KEY=VALUE
27168        for param in &e.params {
27169            self.write_space();
27170            self.write(&param.name);
27171            if let Some(ref value) = param.value {
27172                self.write("=");
27173                self.generate_expression(value)?;
27174            }
27175        }
27176
27177        Ok(())
27178    }
27179
27180    fn generate_quantile(&mut self, e: &Quantile) -> Result<()> {
27181        // QUANTILE(this, quantile)
27182        self.write_keyword("QUANTILE");
27183        self.write("(");
27184        self.generate_expression(&e.this)?;
27185        if let Some(quantile) = &e.quantile {
27186            self.write(", ");
27187            self.generate_expression(quantile)?;
27188        }
27189        self.write(")");
27190        Ok(())
27191    }
27192
27193    fn generate_query_band(&mut self, e: &QueryBand) -> Result<()> {
27194        // QUERY_BAND = this [UPDATE] [FOR scope]
27195        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Teradata)) {
27196            self.write_keyword("SET");
27197            self.write_space();
27198        }
27199        self.write_keyword("QUERY_BAND");
27200        self.write(" = ");
27201        self.generate_expression(&e.this)?;
27202        if e.update.is_some() {
27203            self.write_space();
27204            self.write_keyword("UPDATE");
27205        }
27206        if let Some(scope) = &e.scope {
27207            self.write_space();
27208            self.write_keyword("FOR");
27209            self.write_space();
27210            self.generate_expression(scope)?;
27211        }
27212        Ok(())
27213    }
27214
27215    fn generate_query_option(&mut self, e: &QueryOption) -> Result<()> {
27216        // this = expression
27217        self.generate_expression(&e.this)?;
27218        if let Some(expression) = &e.expression {
27219            self.write(" = ");
27220            self.generate_expression(expression)?;
27221        }
27222        Ok(())
27223    }
27224
27225    fn generate_query_transform(&mut self, e: &QueryTransform) -> Result<()> {
27226        // TRANSFORM (expressions) [row_format_before] [RECORDWRITER record_writer] USING command_script [AS schema] [row_format_after] [RECORDREADER record_reader]
27227        self.write_keyword("TRANSFORM");
27228        self.write("(");
27229        for (i, expr) in e.expressions.iter().enumerate() {
27230            if i > 0 {
27231                self.write(", ");
27232            }
27233            self.generate_expression(expr)?;
27234        }
27235        self.write(")");
27236        if let Some(row_format_before) = &e.row_format_before {
27237            self.write_space();
27238            self.generate_expression(row_format_before)?;
27239        }
27240        if let Some(record_writer) = &e.record_writer {
27241            self.write_space();
27242            self.write_keyword("RECORDWRITER");
27243            self.write_space();
27244            self.generate_expression(record_writer)?;
27245        }
27246        if let Some(command_script) = &e.command_script {
27247            self.write_space();
27248            self.write_keyword("USING");
27249            self.write_space();
27250            self.generate_expression(command_script)?;
27251        }
27252        if let Some(schema) = &e.schema {
27253            self.write_space();
27254            self.write_keyword("AS");
27255            self.write_space();
27256            self.generate_expression(schema)?;
27257        }
27258        if let Some(row_format_after) = &e.row_format_after {
27259            self.write_space();
27260            self.generate_expression(row_format_after)?;
27261        }
27262        if let Some(record_reader) = &e.record_reader {
27263            self.write_space();
27264            self.write_keyword("RECORDREADER");
27265            self.write_space();
27266            self.generate_expression(record_reader)?;
27267        }
27268        Ok(())
27269    }
27270
27271    fn generate_randn(&mut self, e: &Randn) -> Result<()> {
27272        // RANDN([seed])
27273        self.write_keyword("RANDN");
27274        self.write("(");
27275        if let Some(this) = &e.this {
27276            self.generate_expression(this)?;
27277        }
27278        self.write(")");
27279        Ok(())
27280    }
27281
27282    fn generate_randstr(&mut self, e: &Randstr) -> Result<()> {
27283        // RANDSTR(this, [generator])
27284        self.write_keyword("RANDSTR");
27285        self.write("(");
27286        self.generate_expression(&e.this)?;
27287        if let Some(generator) = &e.generator {
27288            self.write(", ");
27289            self.generate_expression(generator)?;
27290        }
27291        self.write(")");
27292        Ok(())
27293    }
27294
27295    fn generate_range_bucket(&mut self, e: &RangeBucket) -> Result<()> {
27296        // RANGE_BUCKET(this, expression)
27297        self.write_keyword("RANGE_BUCKET");
27298        self.write("(");
27299        self.generate_expression(&e.this)?;
27300        self.write(", ");
27301        self.generate_expression(&e.expression)?;
27302        self.write(")");
27303        Ok(())
27304    }
27305
27306    fn generate_range_n(&mut self, e: &RangeN) -> Result<()> {
27307        // RANGE_N(this BETWEEN expressions [EACH each])
27308        self.write_keyword("RANGE_N");
27309        self.write("(");
27310        self.generate_expression(&e.this)?;
27311        self.write_space();
27312        self.write_keyword("BETWEEN");
27313        self.write_space();
27314        for (i, expr) in e.expressions.iter().enumerate() {
27315            if i > 0 {
27316                self.write(", ");
27317            }
27318            self.generate_expression(expr)?;
27319        }
27320        if let Some(each) = &e.each {
27321            self.write_space();
27322            self.write_keyword("EACH");
27323            self.write_space();
27324            self.generate_expression(each)?;
27325        }
27326        self.write(")");
27327        Ok(())
27328    }
27329
27330    fn generate_read_csv(&mut self, e: &ReadCSV) -> Result<()> {
27331        // READ_CSV(this, expressions...)
27332        self.write_keyword("READ_CSV");
27333        self.write("(");
27334        self.generate_expression(&e.this)?;
27335        for expr in &e.expressions {
27336            self.write(", ");
27337            self.generate_expression(expr)?;
27338        }
27339        self.write(")");
27340        Ok(())
27341    }
27342
27343    fn generate_read_parquet(&mut self, e: &ReadParquet) -> Result<()> {
27344        // READ_PARQUET(expressions...)
27345        self.write_keyword("READ_PARQUET");
27346        self.write("(");
27347        for (i, expr) in e.expressions.iter().enumerate() {
27348            if i > 0 {
27349                self.write(", ");
27350            }
27351            self.generate_expression(expr)?;
27352        }
27353        self.write(")");
27354        Ok(())
27355    }
27356
27357    fn generate_recursive_with_search(&mut self, e: &RecursiveWithSearch) -> Result<()> {
27358        // SEARCH kind FIRST BY this SET expression [USING using]
27359        // or CYCLE this SET expression [USING using]
27360        if e.kind == "CYCLE" {
27361            self.write_keyword("CYCLE");
27362        } else {
27363            self.write_keyword("SEARCH");
27364            self.write_space();
27365            self.write(&e.kind);
27366            self.write_space();
27367            self.write_keyword("FIRST BY");
27368        }
27369        self.write_space();
27370        self.generate_expression(&e.this)?;
27371        self.write_space();
27372        self.write_keyword("SET");
27373        self.write_space();
27374        self.generate_expression(&e.expression)?;
27375        if let Some(using) = &e.using {
27376            self.write_space();
27377            self.write_keyword("USING");
27378            self.write_space();
27379            self.generate_expression(using)?;
27380        }
27381        Ok(())
27382    }
27383
27384    fn generate_reduce(&mut self, e: &Reduce) -> Result<()> {
27385        // REDUCE(this, initial, merge, [finish])
27386        self.write_keyword("REDUCE");
27387        self.write("(");
27388        self.generate_expression(&e.this)?;
27389        if let Some(initial) = &e.initial {
27390            self.write(", ");
27391            self.generate_expression(initial)?;
27392        }
27393        if let Some(merge) = &e.merge {
27394            self.write(", ");
27395            self.generate_expression(merge)?;
27396        }
27397        if let Some(finish) = &e.finish {
27398            self.write(", ");
27399            self.generate_expression(finish)?;
27400        }
27401        self.write(")");
27402        Ok(())
27403    }
27404
27405    fn generate_reference(&mut self, e: &Reference) -> Result<()> {
27406        // REFERENCES this (expressions) [options]
27407        self.write_keyword("REFERENCES");
27408        self.write_space();
27409        self.generate_expression(&e.this)?;
27410        if !e.expressions.is_empty() {
27411            self.write(" (");
27412            for (i, expr) in e.expressions.iter().enumerate() {
27413                if i > 0 {
27414                    self.write(", ");
27415                }
27416                self.generate_expression(expr)?;
27417            }
27418            self.write(")");
27419        }
27420        for opt in &e.options {
27421            self.write_space();
27422            self.generate_expression(opt)?;
27423        }
27424        Ok(())
27425    }
27426
27427    fn generate_refresh(&mut self, e: &Refresh) -> Result<()> {
27428        // REFRESH [kind] this
27429        self.write_keyword("REFRESH");
27430        if !e.kind.is_empty() {
27431            self.write_space();
27432            self.write_keyword(&e.kind);
27433        }
27434        self.write_space();
27435        self.generate_expression(&e.this)?;
27436        Ok(())
27437    }
27438
27439    fn generate_refresh_trigger_property(&mut self, e: &RefreshTriggerProperty) -> Result<()> {
27440        // Doris REFRESH clause: REFRESH method ON kind [EVERY n UNIT] [STARTS 'datetime']
27441        self.write_keyword("REFRESH");
27442        self.write_space();
27443        self.write_keyword(&e.method);
27444
27445        if let Some(ref kind) = e.kind {
27446            self.write_space();
27447            self.write_keyword("ON");
27448            self.write_space();
27449            self.write_keyword(kind);
27450
27451            // EVERY n UNIT
27452            if let Some(ref every) = e.every {
27453                self.write_space();
27454                self.write_keyword("EVERY");
27455                self.write_space();
27456                self.generate_expression(every)?;
27457                if let Some(ref unit) = e.unit {
27458                    self.write_space();
27459                    self.write_keyword(unit);
27460                }
27461            }
27462
27463            // STARTS 'datetime'
27464            if let Some(ref starts) = e.starts {
27465                self.write_space();
27466                self.write_keyword("STARTS");
27467                self.write_space();
27468                self.generate_expression(starts)?;
27469            }
27470        }
27471        Ok(())
27472    }
27473
27474    fn generate_regexp_count(&mut self, e: &RegexpCount) -> Result<()> {
27475        // REGEXP_COUNT(this, expression, position, parameters)
27476        self.write_keyword("REGEXP_COUNT");
27477        self.write("(");
27478        self.generate_expression(&e.this)?;
27479        self.write(", ");
27480        self.generate_expression(&e.expression)?;
27481        if let Some(position) = &e.position {
27482            self.write(", ");
27483            self.generate_expression(position)?;
27484        }
27485        if let Some(parameters) = &e.parameters {
27486            self.write(", ");
27487            self.generate_expression(parameters)?;
27488        }
27489        self.write(")");
27490        Ok(())
27491    }
27492
27493    fn generate_regexp_extract_all(&mut self, e: &RegexpExtractAll) -> Result<()> {
27494        // REGEXP_EXTRACT_ALL(this, expression, group, parameters, position, occurrence)
27495        self.write_keyword("REGEXP_EXTRACT_ALL");
27496        self.write("(");
27497        self.generate_expression(&e.this)?;
27498        self.write(", ");
27499        self.generate_expression(&e.expression)?;
27500        if let Some(group) = &e.group {
27501            self.write(", ");
27502            self.generate_expression(group)?;
27503        }
27504        self.write(")");
27505        Ok(())
27506    }
27507
27508    fn generate_regexp_full_match(&mut self, e: &RegexpFullMatch) -> Result<()> {
27509        // REGEXP_FULL_MATCH(this, expression)
27510        self.write_keyword("REGEXP_FULL_MATCH");
27511        self.write("(");
27512        self.generate_expression(&e.this)?;
27513        self.write(", ");
27514        self.generate_expression(&e.expression)?;
27515        self.write(")");
27516        Ok(())
27517    }
27518
27519    fn generate_regexp_i_like(&mut self, e: &RegexpILike) -> Result<()> {
27520        use crate::dialects::DialectType;
27521        // PostgreSQL/Redshift uses ~* operator for case-insensitive regex matching
27522        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) && e.flag.is_none() {
27523            self.generate_expression(&e.this)?;
27524            self.write(" ~* ");
27525            self.generate_expression(&e.expression)?;
27526        } else if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
27527            // Snowflake uses REGEXP_LIKE(x, pattern, 'i')
27528            self.write_keyword("REGEXP_LIKE");
27529            self.write("(");
27530            self.generate_expression(&e.this)?;
27531            self.write(", ");
27532            self.generate_expression(&e.expression)?;
27533            self.write(", ");
27534            if let Some(flag) = &e.flag {
27535                self.generate_expression(flag)?;
27536            } else {
27537                self.write("'i'");
27538            }
27539            self.write(")");
27540        } else {
27541            // this REGEXP_ILIKE expression or REGEXP_ILIKE(this, expression, flag)
27542            self.generate_expression(&e.this)?;
27543            self.write_space();
27544            self.write_keyword("REGEXP_ILIKE");
27545            self.write_space();
27546            self.generate_expression(&e.expression)?;
27547            if let Some(flag) = &e.flag {
27548                self.write(", ");
27549                self.generate_expression(flag)?;
27550            }
27551        }
27552        Ok(())
27553    }
27554
27555    fn generate_regexp_instr(&mut self, e: &RegexpInstr) -> Result<()> {
27556        // REGEXP_INSTR(this, expression, position, occurrence, option, parameters, group)
27557        self.write_keyword("REGEXP_INSTR");
27558        self.write("(");
27559        self.generate_expression(&e.this)?;
27560        self.write(", ");
27561        self.generate_expression(&e.expression)?;
27562        if let Some(position) = &e.position {
27563            self.write(", ");
27564            self.generate_expression(position)?;
27565        }
27566        if let Some(occurrence) = &e.occurrence {
27567            self.write(", ");
27568            self.generate_expression(occurrence)?;
27569        }
27570        if let Some(option) = &e.option {
27571            self.write(", ");
27572            self.generate_expression(option)?;
27573        }
27574        if let Some(parameters) = &e.parameters {
27575            self.write(", ");
27576            self.generate_expression(parameters)?;
27577        }
27578        if let Some(group) = &e.group {
27579            self.write(", ");
27580            self.generate_expression(group)?;
27581        }
27582        self.write(")");
27583        Ok(())
27584    }
27585
27586    fn generate_regexp_split(&mut self, e: &RegexpSplit) -> Result<()> {
27587        // REGEXP_SPLIT(this, expression, limit)
27588        self.write_keyword("REGEXP_SPLIT");
27589        self.write("(");
27590        self.generate_expression(&e.this)?;
27591        self.write(", ");
27592        self.generate_expression(&e.expression)?;
27593        if let Some(limit) = &e.limit {
27594            self.write(", ");
27595            self.generate_expression(limit)?;
27596        }
27597        self.write(")");
27598        Ok(())
27599    }
27600
27601    fn generate_regr_avgx(&mut self, e: &RegrAvgx) -> Result<()> {
27602        // REGR_AVGX(this, expression)
27603        self.write_keyword("REGR_AVGX");
27604        self.write("(");
27605        self.generate_expression(&e.this)?;
27606        self.write(", ");
27607        self.generate_expression(&e.expression)?;
27608        self.write(")");
27609        Ok(())
27610    }
27611
27612    fn generate_regr_avgy(&mut self, e: &RegrAvgy) -> Result<()> {
27613        // REGR_AVGY(this, expression)
27614        self.write_keyword("REGR_AVGY");
27615        self.write("(");
27616        self.generate_expression(&e.this)?;
27617        self.write(", ");
27618        self.generate_expression(&e.expression)?;
27619        self.write(")");
27620        Ok(())
27621    }
27622
27623    fn generate_regr_count(&mut self, e: &RegrCount) -> Result<()> {
27624        // REGR_COUNT(this, expression)
27625        self.write_keyword("REGR_COUNT");
27626        self.write("(");
27627        self.generate_expression(&e.this)?;
27628        self.write(", ");
27629        self.generate_expression(&e.expression)?;
27630        self.write(")");
27631        Ok(())
27632    }
27633
27634    fn generate_regr_intercept(&mut self, e: &RegrIntercept) -> Result<()> {
27635        // REGR_INTERCEPT(this, expression)
27636        self.write_keyword("REGR_INTERCEPT");
27637        self.write("(");
27638        self.generate_expression(&e.this)?;
27639        self.write(", ");
27640        self.generate_expression(&e.expression)?;
27641        self.write(")");
27642        Ok(())
27643    }
27644
27645    fn generate_regr_r2(&mut self, e: &RegrR2) -> Result<()> {
27646        // REGR_R2(this, expression)
27647        self.write_keyword("REGR_R2");
27648        self.write("(");
27649        self.generate_expression(&e.this)?;
27650        self.write(", ");
27651        self.generate_expression(&e.expression)?;
27652        self.write(")");
27653        Ok(())
27654    }
27655
27656    fn generate_regr_slope(&mut self, e: &RegrSlope) -> Result<()> {
27657        // REGR_SLOPE(this, expression)
27658        self.write_keyword("REGR_SLOPE");
27659        self.write("(");
27660        self.generate_expression(&e.this)?;
27661        self.write(", ");
27662        self.generate_expression(&e.expression)?;
27663        self.write(")");
27664        Ok(())
27665    }
27666
27667    fn generate_regr_sxx(&mut self, e: &RegrSxx) -> Result<()> {
27668        // REGR_SXX(this, expression)
27669        self.write_keyword("REGR_SXX");
27670        self.write("(");
27671        self.generate_expression(&e.this)?;
27672        self.write(", ");
27673        self.generate_expression(&e.expression)?;
27674        self.write(")");
27675        Ok(())
27676    }
27677
27678    fn generate_regr_sxy(&mut self, e: &RegrSxy) -> Result<()> {
27679        // REGR_SXY(this, expression)
27680        self.write_keyword("REGR_SXY");
27681        self.write("(");
27682        self.generate_expression(&e.this)?;
27683        self.write(", ");
27684        self.generate_expression(&e.expression)?;
27685        self.write(")");
27686        Ok(())
27687    }
27688
27689    fn generate_regr_syy(&mut self, e: &RegrSyy) -> Result<()> {
27690        // REGR_SYY(this, expression)
27691        self.write_keyword("REGR_SYY");
27692        self.write("(");
27693        self.generate_expression(&e.this)?;
27694        self.write(", ");
27695        self.generate_expression(&e.expression)?;
27696        self.write(")");
27697        Ok(())
27698    }
27699
27700    fn generate_regr_valx(&mut self, e: &RegrValx) -> Result<()> {
27701        // REGR_VALX(this, expression)
27702        self.write_keyword("REGR_VALX");
27703        self.write("(");
27704        self.generate_expression(&e.this)?;
27705        self.write(", ");
27706        self.generate_expression(&e.expression)?;
27707        self.write(")");
27708        Ok(())
27709    }
27710
27711    fn generate_regr_valy(&mut self, e: &RegrValy) -> Result<()> {
27712        // REGR_VALY(this, expression)
27713        self.write_keyword("REGR_VALY");
27714        self.write("(");
27715        self.generate_expression(&e.this)?;
27716        self.write(", ");
27717        self.generate_expression(&e.expression)?;
27718        self.write(")");
27719        Ok(())
27720    }
27721
27722    fn generate_remote_with_connection_model_property(&mut self, e: &RemoteWithConnectionModelProperty) -> Result<()> {
27723        // REMOTE WITH CONNECTION this
27724        self.write_keyword("REMOTE WITH CONNECTION");
27725        self.write_space();
27726        self.generate_expression(&e.this)?;
27727        Ok(())
27728    }
27729
27730    fn generate_rename_column(&mut self, e: &RenameColumn) -> Result<()> {
27731        // RENAME COLUMN [IF EXISTS] this TO new_name
27732        self.write_keyword("RENAME COLUMN");
27733        if e.exists {
27734            self.write_space();
27735            self.write_keyword("IF EXISTS");
27736        }
27737        self.write_space();
27738        self.generate_expression(&e.this)?;
27739        if let Some(to) = &e.to {
27740            self.write_space();
27741            self.write_keyword("TO");
27742            self.write_space();
27743            self.generate_expression(to)?;
27744        }
27745        Ok(())
27746    }
27747
27748    fn generate_replace_partition(&mut self, e: &ReplacePartition) -> Result<()> {
27749        // REPLACE PARTITION expression [FROM source]
27750        self.write_keyword("REPLACE PARTITION");
27751        self.write_space();
27752        self.generate_expression(&e.expression)?;
27753        if let Some(source) = &e.source {
27754            self.write_space();
27755            self.write_keyword("FROM");
27756            self.write_space();
27757            self.generate_expression(source)?;
27758        }
27759        Ok(())
27760    }
27761
27762    fn generate_returning(&mut self, e: &Returning) -> Result<()> {
27763        // RETURNING expressions [INTO into]
27764        // TSQL and Fabric use OUTPUT instead of RETURNING
27765        let keyword = match self.config.dialect {
27766            Some(DialectType::TSQL) | Some(DialectType::Fabric) => "OUTPUT",
27767            _ => "RETURNING",
27768        };
27769        self.write_keyword(keyword);
27770        self.write_space();
27771        for (i, expr) in e.expressions.iter().enumerate() {
27772            if i > 0 {
27773                self.write(", ");
27774            }
27775            self.generate_expression(expr)?;
27776        }
27777        if let Some(into) = &e.into {
27778            self.write_space();
27779            self.write_keyword("INTO");
27780            self.write_space();
27781            self.generate_expression(into)?;
27782        }
27783        Ok(())
27784    }
27785
27786    fn generate_output_clause(&mut self, output: &OutputClause) -> Result<()> {
27787        // OUTPUT expressions [INTO into_table]
27788        self.write_space();
27789        self.write_keyword("OUTPUT");
27790        self.write_space();
27791        for (i, expr) in output.columns.iter().enumerate() {
27792            if i > 0 {
27793                self.write(", ");
27794            }
27795            self.generate_expression(expr)?;
27796        }
27797        if let Some(into_table) = &output.into_table {
27798            self.write_space();
27799            self.write_keyword("INTO");
27800            self.write_space();
27801            self.generate_expression(into_table)?;
27802        }
27803        Ok(())
27804    }
27805
27806    fn generate_returns_property(&mut self, e: &ReturnsProperty) -> Result<()> {
27807        // RETURNS [TABLE] this [NULL ON NULL INPUT | CALLED ON NULL INPUT]
27808        self.write_keyword("RETURNS");
27809        if e.is_table.is_some() {
27810            self.write_space();
27811            self.write_keyword("TABLE");
27812        }
27813        if let Some(table) = &e.table {
27814            self.write_space();
27815            self.generate_expression(table)?;
27816        } else if let Some(this) = &e.this {
27817            self.write_space();
27818            self.generate_expression(this)?;
27819        }
27820        if e.null.is_some() {
27821            self.write_space();
27822            self.write_keyword("NULL ON NULL INPUT");
27823        }
27824        Ok(())
27825    }
27826
27827    fn generate_rollback(&mut self, e: &Rollback) -> Result<()> {
27828        // ROLLBACK [TRANSACTION [transaction_name]] [TO savepoint]
27829        self.write_keyword("ROLLBACK");
27830
27831        // TSQL always uses ROLLBACK TRANSACTION
27832        if e.this.is_none() && matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
27833            self.write_space();
27834            self.write_keyword("TRANSACTION");
27835        }
27836
27837        // Check if this has TRANSACTION keyword or transaction name
27838        if let Some(this) = &e.this {
27839            // Check if it's just the "TRANSACTION" marker or an actual transaction name
27840            let is_transaction_marker = matches!(
27841                this.as_ref(),
27842                Expression::Identifier(id) if id.name == "TRANSACTION"
27843            );
27844
27845            self.write_space();
27846            self.write_keyword("TRANSACTION");
27847
27848            // If it's a real transaction name, output it
27849            if !is_transaction_marker {
27850                self.write_space();
27851                self.generate_expression(this)?;
27852            }
27853        }
27854
27855        // Output TO savepoint
27856        if let Some(savepoint) = &e.savepoint {
27857            self.write_space();
27858            self.write_keyword("TO");
27859            self.write_space();
27860            self.generate_expression(savepoint)?;
27861        }
27862        Ok(())
27863    }
27864
27865    fn generate_rollup(&mut self, e: &Rollup) -> Result<()> {
27866        // Python: return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
27867        if e.expressions.is_empty() {
27868            self.write_keyword("WITH ROLLUP");
27869        } else {
27870            self.write_keyword("ROLLUP");
27871            self.write("(");
27872            for (i, expr) in e.expressions.iter().enumerate() {
27873                if i > 0 {
27874                    self.write(", ");
27875                }
27876                self.generate_expression(expr)?;
27877            }
27878            self.write(")");
27879        }
27880        Ok(())
27881    }
27882
27883    fn generate_row_format_delimited_property(&mut self, e: &RowFormatDelimitedProperty) -> Result<()> {
27884        // ROW FORMAT DELIMITED [FIELDS TERMINATED BY ...] [ESCAPED BY ...] [COLLECTION ITEMS TERMINATED BY ...] [MAP KEYS TERMINATED BY ...] [LINES TERMINATED BY ...] [NULL DEFINED AS ...]
27885        self.write_keyword("ROW FORMAT DELIMITED");
27886        if let Some(fields) = &e.fields {
27887            self.write_space();
27888            self.write_keyword("FIELDS TERMINATED BY");
27889            self.write_space();
27890            self.generate_expression(fields)?;
27891        }
27892        if let Some(escaped) = &e.escaped {
27893            self.write_space();
27894            self.write_keyword("ESCAPED BY");
27895            self.write_space();
27896            self.generate_expression(escaped)?;
27897        }
27898        if let Some(items) = &e.collection_items {
27899            self.write_space();
27900            self.write_keyword("COLLECTION ITEMS TERMINATED BY");
27901            self.write_space();
27902            self.generate_expression(items)?;
27903        }
27904        if let Some(keys) = &e.map_keys {
27905            self.write_space();
27906            self.write_keyword("MAP KEYS TERMINATED BY");
27907            self.write_space();
27908            self.generate_expression(keys)?;
27909        }
27910        if let Some(lines) = &e.lines {
27911            self.write_space();
27912            self.write_keyword("LINES TERMINATED BY");
27913            self.write_space();
27914            self.generate_expression(lines)?;
27915        }
27916        if let Some(null) = &e.null {
27917            self.write_space();
27918            self.write_keyword("NULL DEFINED AS");
27919            self.write_space();
27920            self.generate_expression(null)?;
27921        }
27922        if let Some(serde) = &e.serde {
27923            self.write_space();
27924            self.generate_expression(serde)?;
27925        }
27926        Ok(())
27927    }
27928
27929    fn generate_row_format_property(&mut self, e: &RowFormatProperty) -> Result<()> {
27930        // ROW FORMAT this
27931        self.write_keyword("ROW FORMAT");
27932        self.write_space();
27933        self.generate_expression(&e.this)?;
27934        Ok(())
27935    }
27936
27937    fn generate_row_format_serde_property(&mut self, e: &RowFormatSerdeProperty) -> Result<()> {
27938        // ROW FORMAT SERDE this [WITH SERDEPROPERTIES (...)]
27939        self.write_keyword("ROW FORMAT SERDE");
27940        self.write_space();
27941        self.generate_expression(&e.this)?;
27942        if let Some(props) = &e.serde_properties {
27943            self.write_space();
27944            // SerdeProperties generates its own "[WITH] SERDEPROPERTIES (...)"
27945            self.generate_expression(props)?;
27946        }
27947        Ok(())
27948    }
27949
27950    fn generate_sha2(&mut self, e: &SHA2) -> Result<()> {
27951        // SHA2(this, length)
27952        self.write_keyword("SHA2");
27953        self.write("(");
27954        self.generate_expression(&e.this)?;
27955        if let Some(length) = e.length {
27956            self.write(", ");
27957            self.write(&length.to_string());
27958        }
27959        self.write(")");
27960        Ok(())
27961    }
27962
27963    fn generate_sha2_digest(&mut self, e: &SHA2Digest) -> Result<()> {
27964        // SHA2_DIGEST(this, length)
27965        self.write_keyword("SHA2_DIGEST");
27966        self.write("(");
27967        self.generate_expression(&e.this)?;
27968        if let Some(length) = e.length {
27969            self.write(", ");
27970            self.write(&length.to_string());
27971        }
27972        self.write(")");
27973        Ok(())
27974    }
27975
27976    fn generate_safe_add(&mut self, e: &SafeAdd) -> Result<()> {
27977        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
27978            "TRY_ADD"
27979        } else {
27980            "SAFE_ADD"
27981        };
27982        self.write_keyword(name);
27983        self.write("(");
27984        self.generate_expression(&e.this)?;
27985        self.write(", ");
27986        self.generate_expression(&e.expression)?;
27987        self.write(")");
27988        Ok(())
27989    }
27990
27991    fn generate_safe_divide(&mut self, e: &SafeDivide) -> Result<()> {
27992        // SAFE_DIVIDE(this, expression)
27993        self.write_keyword("SAFE_DIVIDE");
27994        self.write("(");
27995        self.generate_expression(&e.this)?;
27996        self.write(", ");
27997        self.generate_expression(&e.expression)?;
27998        self.write(")");
27999        Ok(())
28000    }
28001
28002    fn generate_safe_multiply(&mut self, e: &SafeMultiply) -> Result<()> {
28003        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
28004            "TRY_MULTIPLY"
28005        } else {
28006            "SAFE_MULTIPLY"
28007        };
28008        self.write_keyword(name);
28009        self.write("(");
28010        self.generate_expression(&e.this)?;
28011        self.write(", ");
28012        self.generate_expression(&e.expression)?;
28013        self.write(")");
28014        Ok(())
28015    }
28016
28017    fn generate_safe_subtract(&mut self, e: &SafeSubtract) -> Result<()> {
28018        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
28019            "TRY_SUBTRACT"
28020        } else {
28021            "SAFE_SUBTRACT"
28022        };
28023        self.write_keyword(name);
28024        self.write("(");
28025        self.generate_expression(&e.this)?;
28026        self.write(", ");
28027        self.generate_expression(&e.expression)?;
28028        self.write(")");
28029        Ok(())
28030    }
28031
28032    /// Generate the body of a USING SAMPLE or TABLESAMPLE clause:
28033    /// METHOD (size UNIT) [REPEATABLE (seed)]
28034    fn generate_sample_body(&mut self, sample: &Sample) -> Result<()> {
28035        // Handle BUCKET sampling: TABLESAMPLE (BUCKET n OUT OF m [ON col])
28036        if matches!(sample.method, SampleMethod::Bucket) {
28037            self.write(" (");
28038            self.write_keyword("BUCKET");
28039            self.write_space();
28040            if let Some(ref num) = sample.bucket_numerator {
28041                self.generate_expression(num)?;
28042            }
28043            self.write_space();
28044            self.write_keyword("OUT OF");
28045            self.write_space();
28046            if let Some(ref denom) = sample.bucket_denominator {
28047                self.generate_expression(denom)?;
28048            }
28049            if let Some(ref field) = sample.bucket_field {
28050                self.write_space();
28051                self.write_keyword("ON");
28052                self.write_space();
28053                self.generate_expression(field)?;
28054            }
28055            self.write(")");
28056            return Ok(());
28057        }
28058
28059        // Output method name if explicitly specified, or for dialects that always require it
28060        let is_snowflake = matches!(self.config.dialect, Some(crate::dialects::DialectType::Snowflake));
28061        let is_postgres = matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL) | Some(crate::dialects::DialectType::Redshift));
28062        // Databricks and Spark don't output method names
28063        let is_databricks = matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks));
28064        let is_spark = matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark));
28065        let suppress_method = is_databricks || is_spark || sample.suppress_method_output;
28066        // PostgreSQL always outputs BERNOULLI for BERNOULLI samples
28067        let force_method = is_postgres && matches!(sample.method, SampleMethod::Bernoulli);
28068        if !suppress_method && (sample.explicit_method || is_snowflake || force_method) {
28069            self.write_space();
28070            if !sample.explicit_method && (is_snowflake || force_method) {
28071                // Snowflake/PostgreSQL defaults to BERNOULLI when no method is specified
28072                self.write_keyword("BERNOULLI");
28073            } else {
28074                match sample.method {
28075                    SampleMethod::Bernoulli => self.write_keyword("BERNOULLI"),
28076                    SampleMethod::System => self.write_keyword("SYSTEM"),
28077                    SampleMethod::Block => self.write_keyword("BLOCK"),
28078                    SampleMethod::Row => self.write_keyword("ROW"),
28079                    SampleMethod::Reservoir => self.write_keyword("RESERVOIR"),
28080                    SampleMethod::Percent => self.write_keyword("SYSTEM"),
28081                    SampleMethod::Bucket => {} // handled above
28082                }
28083            }
28084        }
28085
28086        // Output size, with or without parentheses depending on dialect
28087        let emit_size_no_parens = !self.config.tablesample_requires_parens;
28088        if emit_size_no_parens {
28089            self.write_space();
28090            match &sample.size {
28091                Expression::Tuple(tuple) => {
28092                    for (i, expr) in tuple.expressions.iter().enumerate() {
28093                        if i > 0 {
28094                            self.write(", ");
28095                        }
28096                        self.generate_expression(expr)?;
28097                    }
28098                }
28099                expr => self.generate_expression(expr)?,
28100            }
28101        } else {
28102            self.write(" (");
28103            self.generate_expression(&sample.size)?;
28104        }
28105
28106        // Determine unit
28107        let is_rows_method = matches!(sample.method, SampleMethod::Reservoir | SampleMethod::Row | SampleMethod::Bucket);
28108        let is_percent = matches!(sample.method, SampleMethod::Percent | SampleMethod::System | SampleMethod::Bernoulli | SampleMethod::Block);
28109
28110        // For Snowflake, PostgreSQL, and Presto/Trino, only output ROWS/PERCENT when the user explicitly wrote it (unit_after_size).
28111        // These dialects use bare numbers for percentage by default in TABLESAMPLE METHOD(size) syntax.
28112        // For Databricks and Spark, always output PERCENT for percentage samples.
28113        let is_presto = matches!(self.config.dialect, Some(crate::dialects::DialectType::Presto) | Some(crate::dialects::DialectType::Trino) | Some(crate::dialects::DialectType::Athena));
28114        let should_output_unit = if is_databricks || is_spark {
28115            // Always output PERCENT for percentage-based methods, or ROWS for row-based methods
28116            is_percent || is_rows_method || sample.unit_after_size
28117        } else if is_snowflake || is_postgres || is_presto {
28118            sample.unit_after_size
28119        } else {
28120            sample.unit_after_size || (sample.explicit_method && (is_rows_method || is_percent))
28121        };
28122
28123        if should_output_unit {
28124            self.write_space();
28125            if sample.is_percent {
28126                self.write_keyword("PERCENT");
28127            } else if is_rows_method && !sample.unit_after_size {
28128                self.write_keyword("ROWS");
28129            } else if sample.unit_after_size {
28130                match sample.method {
28131                    SampleMethod::Percent | SampleMethod::System | SampleMethod::Bernoulli | SampleMethod::Block => {
28132                        self.write_keyword("PERCENT");
28133                    }
28134                    SampleMethod::Row | SampleMethod::Reservoir => {
28135                        self.write_keyword("ROWS");
28136                    }
28137                    _ => self.write_keyword("ROWS"),
28138                }
28139            } else {
28140                self.write_keyword("PERCENT");
28141            }
28142        }
28143
28144        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28145            if let Some(ref offset) = sample.offset {
28146                self.write_space();
28147                self.write_keyword("OFFSET");
28148                self.write_space();
28149                self.generate_expression(offset)?;
28150            }
28151        }
28152        if !emit_size_no_parens {
28153            self.write(")");
28154        }
28155
28156        Ok(())
28157    }
28158
28159    fn generate_sample_property(&mut self, e: &SampleProperty) -> Result<()> {
28160        // SAMPLE this (ClickHouse uses SAMPLE BY)
28161        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28162            self.write_keyword("SAMPLE BY");
28163        } else {
28164            self.write_keyword("SAMPLE");
28165        }
28166        self.write_space();
28167        self.generate_expression(&e.this)?;
28168        Ok(())
28169    }
28170
28171    fn generate_schema(&mut self, e: &Schema) -> Result<()> {
28172        // this (expressions...)
28173        if let Some(this) = &e.this {
28174            self.generate_expression(this)?;
28175        }
28176        if !e.expressions.is_empty() {
28177            // Add space before column list if there's a preceding expression
28178            if e.this.is_some() {
28179                self.write_space();
28180            }
28181            self.write("(");
28182            for (i, expr) in e.expressions.iter().enumerate() {
28183                if i > 0 {
28184                    self.write(", ");
28185                }
28186                self.generate_expression(expr)?;
28187            }
28188            self.write(")");
28189        }
28190        Ok(())
28191    }
28192
28193    fn generate_schema_comment_property(&mut self, e: &SchemaCommentProperty) -> Result<()> {
28194        // COMMENT this
28195        self.write_keyword("COMMENT");
28196        self.write_space();
28197        self.generate_expression(&e.this)?;
28198        Ok(())
28199    }
28200
28201    fn generate_scope_resolution(&mut self, e: &ScopeResolution) -> Result<()> {
28202        // [this::]expression
28203        if let Some(this) = &e.this {
28204            self.generate_expression(this)?;
28205            self.write("::");
28206        }
28207        self.generate_expression(&e.expression)?;
28208        Ok(())
28209    }
28210
28211    fn generate_search(&mut self, e: &Search) -> Result<()> {
28212        // SEARCH(this, expression, [json_scope], [analyzer], [analyzer_options], [search_mode])
28213        self.write_keyword("SEARCH");
28214        self.write("(");
28215        self.generate_expression(&e.this)?;
28216        self.write(", ");
28217        self.generate_expression(&e.expression)?;
28218        if let Some(json_scope) = &e.json_scope {
28219            self.write(", ");
28220            self.generate_expression(json_scope)?;
28221        }
28222        if let Some(analyzer) = &e.analyzer {
28223            self.write(", ");
28224            self.generate_expression(analyzer)?;
28225        }
28226        if let Some(analyzer_options) = &e.analyzer_options {
28227            self.write(", ");
28228            self.generate_expression(analyzer_options)?;
28229        }
28230        if let Some(search_mode) = &e.search_mode {
28231            self.write(", ");
28232            self.generate_expression(search_mode)?;
28233        }
28234        self.write(")");
28235        Ok(())
28236    }
28237
28238    fn generate_search_ip(&mut self, e: &SearchIp) -> Result<()> {
28239        // SEARCH_IP(this, expression)
28240        self.write_keyword("SEARCH_IP");
28241        self.write("(");
28242        self.generate_expression(&e.this)?;
28243        self.write(", ");
28244        self.generate_expression(&e.expression)?;
28245        self.write(")");
28246        Ok(())
28247    }
28248
28249    fn generate_security_property(&mut self, e: &SecurityProperty) -> Result<()> {
28250        // SECURITY this
28251        self.write_keyword("SECURITY");
28252        self.write_space();
28253        self.generate_expression(&e.this)?;
28254        Ok(())
28255    }
28256
28257    fn generate_semantic_view(&mut self, e: &SemanticView) -> Result<()> {
28258        // SEMANTIC_VIEW(this [METRICS ...] [DIMENSIONS ...] [FACTS ...] [WHERE ...])
28259        self.write("SEMANTIC_VIEW(");
28260
28261        if self.config.pretty {
28262            // Pretty print: each clause on its own line
28263            self.write_newline();
28264            self.indent_level += 1;
28265            self.write_indent();
28266            self.generate_expression(&e.this)?;
28267
28268            if let Some(metrics) = &e.metrics {
28269                self.write_newline();
28270                self.write_indent();
28271                self.write_keyword("METRICS");
28272                self.write_space();
28273                self.generate_semantic_view_tuple(metrics)?;
28274            }
28275            if let Some(dimensions) = &e.dimensions {
28276                self.write_newline();
28277                self.write_indent();
28278                self.write_keyword("DIMENSIONS");
28279                self.write_space();
28280                self.generate_semantic_view_tuple(dimensions)?;
28281            }
28282            if let Some(facts) = &e.facts {
28283                self.write_newline();
28284                self.write_indent();
28285                self.write_keyword("FACTS");
28286                self.write_space();
28287                self.generate_semantic_view_tuple(facts)?;
28288            }
28289            if let Some(where_) = &e.where_ {
28290                self.write_newline();
28291                self.write_indent();
28292                self.write_keyword("WHERE");
28293                self.write_space();
28294                self.generate_expression(where_)?;
28295            }
28296            self.write_newline();
28297            self.indent_level -= 1;
28298            self.write_indent();
28299        } else {
28300            // Compact: all on one line
28301            self.generate_expression(&e.this)?;
28302            if let Some(metrics) = &e.metrics {
28303                self.write_space();
28304                self.write_keyword("METRICS");
28305                self.write_space();
28306                self.generate_semantic_view_tuple(metrics)?;
28307            }
28308            if let Some(dimensions) = &e.dimensions {
28309                self.write_space();
28310                self.write_keyword("DIMENSIONS");
28311                self.write_space();
28312                self.generate_semantic_view_tuple(dimensions)?;
28313            }
28314            if let Some(facts) = &e.facts {
28315                self.write_space();
28316                self.write_keyword("FACTS");
28317                self.write_space();
28318                self.generate_semantic_view_tuple(facts)?;
28319            }
28320            if let Some(where_) = &e.where_ {
28321                self.write_space();
28322                self.write_keyword("WHERE");
28323                self.write_space();
28324                self.generate_expression(where_)?;
28325            }
28326        }
28327        self.write(")");
28328        Ok(())
28329    }
28330
28331    /// Helper for SEMANTIC_VIEW tuple contents (without parentheses)
28332    fn generate_semantic_view_tuple(&mut self, expr: &Expression) -> Result<()> {
28333        if let Expression::Tuple(t) = expr {
28334            for (i, e) in t.expressions.iter().enumerate() {
28335                if i > 0 {
28336                    self.write(", ");
28337                }
28338                self.generate_expression(e)?;
28339            }
28340        } else {
28341            self.generate_expression(expr)?;
28342        }
28343        Ok(())
28344    }
28345
28346    fn generate_sequence_properties(&mut self, e: &SequenceProperties) -> Result<()> {
28347        // [START WITH start] [INCREMENT BY increment] [MINVALUE minvalue] [MAXVALUE maxvalue] [CACHE cache] [OWNED BY owned]
28348        if let Some(start) = &e.start {
28349            self.write_keyword("START WITH");
28350            self.write_space();
28351            self.generate_expression(start)?;
28352        }
28353        if let Some(increment) = &e.increment {
28354            self.write_space();
28355            self.write_keyword("INCREMENT BY");
28356            self.write_space();
28357            self.generate_expression(increment)?;
28358        }
28359        if let Some(minvalue) = &e.minvalue {
28360            self.write_space();
28361            self.write_keyword("MINVALUE");
28362            self.write_space();
28363            self.generate_expression(minvalue)?;
28364        }
28365        if let Some(maxvalue) = &e.maxvalue {
28366            self.write_space();
28367            self.write_keyword("MAXVALUE");
28368            self.write_space();
28369            self.generate_expression(maxvalue)?;
28370        }
28371        if let Some(cache) = &e.cache {
28372            self.write_space();
28373            self.write_keyword("CACHE");
28374            self.write_space();
28375            self.generate_expression(cache)?;
28376        }
28377        if let Some(owned) = &e.owned {
28378            self.write_space();
28379            self.write_keyword("OWNED BY");
28380            self.write_space();
28381            self.generate_expression(owned)?;
28382        }
28383        for opt in &e.options {
28384            self.write_space();
28385            self.generate_expression(opt)?;
28386        }
28387        Ok(())
28388    }
28389
28390    fn generate_serde_properties(&mut self, e: &SerdeProperties) -> Result<()> {
28391        // [WITH] SERDEPROPERTIES (expressions)
28392        if e.with_.is_some() {
28393            self.write_keyword("WITH");
28394            self.write_space();
28395        }
28396        self.write_keyword("SERDEPROPERTIES");
28397        self.write(" (");
28398        for (i, expr) in e.expressions.iter().enumerate() {
28399            if i > 0 {
28400                self.write(", ");
28401            }
28402            // Generate key=value without spaces around =
28403            match expr {
28404                Expression::Eq(eq) => {
28405                    self.generate_expression(&eq.left)?;
28406                    self.write("=");
28407                    self.generate_expression(&eq.right)?;
28408                }
28409                _ => self.generate_expression(expr)?,
28410            }
28411        }
28412        self.write(")");
28413        Ok(())
28414    }
28415
28416    fn generate_session_parameter(&mut self, e: &SessionParameter) -> Result<()> {
28417        // @@[kind.]this
28418        self.write("@@");
28419        if let Some(kind) = &e.kind {
28420            self.write(kind);
28421            self.write(".");
28422        }
28423        self.generate_expression(&e.this)?;
28424        Ok(())
28425    }
28426
28427    fn generate_set(&mut self, e: &Set) -> Result<()> {
28428        // SET/UNSET [TAG] expressions
28429        if e.unset.is_some() {
28430            self.write_keyword("UNSET");
28431        } else {
28432            self.write_keyword("SET");
28433        }
28434        if e.tag.is_some() {
28435            self.write_space();
28436            self.write_keyword("TAG");
28437        }
28438        if !e.expressions.is_empty() {
28439            self.write_space();
28440            for (i, expr) in e.expressions.iter().enumerate() {
28441                if i > 0 {
28442                    self.write(", ");
28443                }
28444                self.generate_expression(expr)?;
28445            }
28446        }
28447        Ok(())
28448    }
28449
28450    fn generate_set_config_property(&mut self, e: &SetConfigProperty) -> Result<()> {
28451        // SET this or SETCONFIG this
28452        self.write_keyword("SET");
28453        self.write_space();
28454        self.generate_expression(&e.this)?;
28455        Ok(())
28456    }
28457
28458    fn generate_set_item(&mut self, e: &SetItem) -> Result<()> {
28459        // [kind] name = value
28460        if let Some(kind) = &e.kind {
28461            self.write_keyword(kind);
28462            self.write_space();
28463        }
28464        self.generate_expression(&e.name)?;
28465        self.write(" = ");
28466        self.generate_expression(&e.value)?;
28467        Ok(())
28468    }
28469
28470    fn generate_set_operation(&mut self, e: &SetOperation) -> Result<()> {
28471        // [WITH ...] this UNION|INTERSECT|EXCEPT [ALL|DISTINCT] [BY NAME] expression
28472        if let Some(with_) = &e.with_ {
28473            self.generate_expression(with_)?;
28474            self.write_space();
28475        }
28476        self.generate_expression(&e.this)?;
28477        self.write_space();
28478        // kind should be UNION, INTERSECT, EXCEPT, etc.
28479        if let Some(kind) = &e.kind {
28480            self.write_keyword(kind);
28481        }
28482        if e.distinct {
28483            self.write_space();
28484            self.write_keyword("DISTINCT");
28485        } else {
28486            self.write_space();
28487            self.write_keyword("ALL");
28488        }
28489        if e.by_name.is_some() {
28490            self.write_space();
28491            self.write_keyword("BY NAME");
28492        }
28493        self.write_space();
28494        self.generate_expression(&e.expression)?;
28495        Ok(())
28496    }
28497
28498    fn generate_set_property(&mut self, e: &SetProperty) -> Result<()> {
28499        // SET or MULTISET
28500        if e.multi.is_some() {
28501            self.write_keyword("MULTISET");
28502        } else {
28503            self.write_keyword("SET");
28504        }
28505        Ok(())
28506    }
28507
28508    fn generate_settings_property(&mut self, e: &SettingsProperty) -> Result<()> {
28509        // SETTINGS expressions
28510        self.write_keyword("SETTINGS");
28511        if self.config.pretty && e.expressions.len() > 1 {
28512            // Pretty print: each setting on its own line, indented
28513            self.indent_level += 1;
28514            for (i, expr) in e.expressions.iter().enumerate() {
28515                if i > 0 {
28516                    self.write(",");
28517                }
28518                self.write_newline();
28519                self.write_indent();
28520                self.generate_expression(expr)?;
28521            }
28522            self.indent_level -= 1;
28523        } else {
28524            self.write_space();
28525            for (i, expr) in e.expressions.iter().enumerate() {
28526                if i > 0 {
28527                    self.write(", ");
28528                }
28529                self.generate_expression(expr)?;
28530            }
28531        }
28532        Ok(())
28533    }
28534
28535    fn generate_sharing_property(&mut self, e: &SharingProperty) -> Result<()> {
28536        // SHARING = this
28537        self.write_keyword("SHARING");
28538        if let Some(this) = &e.this {
28539            self.write(" = ");
28540            self.generate_expression(this)?;
28541        }
28542        Ok(())
28543    }
28544
28545    fn generate_slice(&mut self, e: &Slice) -> Result<()> {
28546        // Python array slicing: begin:end:step
28547        if let Some(begin) = &e.this {
28548            self.generate_expression(begin)?;
28549        }
28550        self.write(":");
28551        if let Some(end) = &e.expression {
28552            self.generate_expression(end)?;
28553        }
28554        if let Some(step) = &e.step {
28555            self.write(":");
28556            self.generate_expression(step)?;
28557        }
28558        Ok(())
28559    }
28560
28561    fn generate_sort_array(&mut self, e: &SortArray) -> Result<()> {
28562        // SORT_ARRAY(this, asc)
28563        self.write_keyword("SORT_ARRAY");
28564        self.write("(");
28565        self.generate_expression(&e.this)?;
28566        if let Some(asc) = &e.asc {
28567            self.write(", ");
28568            self.generate_expression(asc)?;
28569        }
28570        self.write(")");
28571        Ok(())
28572    }
28573
28574    fn generate_sort_by(&mut self, e: &SortBy) -> Result<()> {
28575        // SORT BY expressions
28576        self.write_keyword("SORT BY");
28577        self.write_space();
28578        for (i, expr) in e.expressions.iter().enumerate() {
28579            if i > 0 {
28580                self.write(", ");
28581            }
28582            self.generate_ordered(expr)?;
28583        }
28584        Ok(())
28585    }
28586
28587    fn generate_sort_key_property(&mut self, e: &SortKeyProperty) -> Result<()> {
28588        // [COMPOUND] SORTKEY(col1, col2, ...) - no space before paren
28589        if e.compound.is_some() {
28590            self.write_keyword("COMPOUND");
28591            self.write_space();
28592        }
28593        self.write_keyword("SORTKEY");
28594        self.write("(");
28595        // If this is a Tuple, unwrap its contents to avoid double parentheses
28596        if let Expression::Tuple(t) = e.this.as_ref() {
28597            for (i, expr) in t.expressions.iter().enumerate() {
28598                if i > 0 {
28599                    self.write(", ");
28600                }
28601                self.generate_expression(expr)?;
28602            }
28603        } else {
28604            self.generate_expression(&e.this)?;
28605        }
28606        self.write(")");
28607        Ok(())
28608    }
28609
28610    fn generate_split_part(&mut self, e: &SplitPart) -> Result<()> {
28611        // SPLIT_PART(this, delimiter, part_index)
28612        self.write_keyword("SPLIT_PART");
28613        self.write("(");
28614        self.generate_expression(&e.this)?;
28615        if let Some(delimiter) = &e.delimiter {
28616            self.write(", ");
28617            self.generate_expression(delimiter)?;
28618        }
28619        if let Some(part_index) = &e.part_index {
28620            self.write(", ");
28621            self.generate_expression(part_index)?;
28622        }
28623        self.write(")");
28624        Ok(())
28625    }
28626
28627    fn generate_sql_read_write_property(&mut self, e: &SqlReadWriteProperty) -> Result<()> {
28628        // READS SQL DATA or MODIFIES SQL DATA, etc.
28629        self.generate_expression(&e.this)?;
28630        Ok(())
28631    }
28632
28633    fn generate_sql_security_property(&mut self, e: &SqlSecurityProperty) -> Result<()> {
28634        // SQL SECURITY DEFINER or SQL SECURITY INVOKER
28635        self.write_keyword("SQL SECURITY");
28636        self.write_space();
28637        self.generate_expression(&e.this)?;
28638        Ok(())
28639    }
28640
28641    fn generate_st_distance(&mut self, e: &StDistance) -> Result<()> {
28642        // ST_DISTANCE(this, expression, [use_spheroid])
28643        self.write_keyword("ST_DISTANCE");
28644        self.write("(");
28645        self.generate_expression(&e.this)?;
28646        self.write(", ");
28647        self.generate_expression(&e.expression)?;
28648        if let Some(use_spheroid) = &e.use_spheroid {
28649            self.write(", ");
28650            self.generate_expression(use_spheroid)?;
28651        }
28652        self.write(")");
28653        Ok(())
28654    }
28655
28656    fn generate_st_point(&mut self, e: &StPoint) -> Result<()> {
28657        // ST_POINT(this, expression)
28658        self.write_keyword("ST_POINT");
28659        self.write("(");
28660        self.generate_expression(&e.this)?;
28661        self.write(", ");
28662        self.generate_expression(&e.expression)?;
28663        self.write(")");
28664        Ok(())
28665    }
28666
28667    fn generate_stability_property(&mut self, e: &StabilityProperty) -> Result<()> {
28668        // IMMUTABLE, STABLE, VOLATILE
28669        self.generate_expression(&e.this)?;
28670        Ok(())
28671    }
28672
28673    fn generate_standard_hash(&mut self, e: &StandardHash) -> Result<()> {
28674        // STANDARD_HASH(this, [expression])
28675        self.write_keyword("STANDARD_HASH");
28676        self.write("(");
28677        self.generate_expression(&e.this)?;
28678        if let Some(expression) = &e.expression {
28679            self.write(", ");
28680            self.generate_expression(expression)?;
28681        }
28682        self.write(")");
28683        Ok(())
28684    }
28685
28686    fn generate_storage_handler_property(&mut self, e: &StorageHandlerProperty) -> Result<()> {
28687        // STORED BY this
28688        self.write_keyword("STORED BY");
28689        self.write_space();
28690        self.generate_expression(&e.this)?;
28691        Ok(())
28692    }
28693
28694    fn generate_str_position(&mut self, e: &StrPosition) -> Result<()> {
28695        // STRPOS(this, substr) or STRPOS(this, substr, position)
28696        // Different dialects have different function names
28697        use crate::dialects::DialectType;
28698        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
28699            // Snowflake: CHARINDEX(substr, str[, position])
28700            self.write_keyword("CHARINDEX");
28701            self.write("(");
28702            if let Some(substr) = &e.substr {
28703                self.generate_expression(substr)?;
28704                self.write(", ");
28705            }
28706            self.generate_expression(&e.this)?;
28707            if let Some(position) = &e.position {
28708                self.write(", ");
28709                self.generate_expression(position)?;
28710            }
28711            self.write(")");
28712        } else if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28713            self.write_keyword("POSITION");
28714            self.write("(");
28715            self.generate_expression(&e.this)?;
28716            if let Some(substr) = &e.substr {
28717                self.write(", ");
28718                self.generate_expression(substr)?;
28719            }
28720            if let Some(position) = &e.position {
28721                self.write(", ");
28722                self.generate_expression(position)?;
28723            }
28724            if let Some(occurrence) = &e.occurrence {
28725                self.write(", ");
28726                self.generate_expression(occurrence)?;
28727            }
28728            self.write(")");
28729        } else if matches!(self.config.dialect, Some(DialectType::SQLite) | Some(DialectType::Oracle) | Some(DialectType::BigQuery) | Some(DialectType::Teradata)) {
28730            self.write_keyword("INSTR");
28731            self.write("(");
28732            self.generate_expression(&e.this)?;
28733            if let Some(substr) = &e.substr {
28734                self.write(", ");
28735                self.generate_expression(substr)?;
28736            }
28737            if let Some(position) = &e.position {
28738                self.write(", ");
28739                self.generate_expression(position)?;
28740            } else if e.occurrence.is_some() {
28741                // INSTR requires a position arg before occurrence: INSTR(str, substr, start, nth)
28742                // Default start position is 1
28743                self.write(", 1");
28744            }
28745            if let Some(occurrence) = &e.occurrence {
28746                self.write(", ");
28747                self.generate_expression(occurrence)?;
28748            }
28749            self.write(")");
28750        } else if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::Doris) | Some(DialectType::StarRocks)
28751            | Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)) {
28752            // LOCATE(substr, str[, position]) - substr first
28753            self.write_keyword("LOCATE");
28754            self.write("(");
28755            if let Some(substr) = &e.substr {
28756                self.generate_expression(substr)?;
28757                self.write(", ");
28758            }
28759            self.generate_expression(&e.this)?;
28760            if let Some(position) = &e.position {
28761                self.write(", ");
28762                self.generate_expression(position)?;
28763            }
28764            self.write(")");
28765        } else if matches!(self.config.dialect, Some(DialectType::TSQL)) {
28766            // CHARINDEX(substr, str[, position])
28767            self.write_keyword("CHARINDEX");
28768            self.write("(");
28769            if let Some(substr) = &e.substr {
28770                self.generate_expression(substr)?;
28771                self.write(", ");
28772            }
28773            self.generate_expression(&e.this)?;
28774            if let Some(position) = &e.position {
28775                self.write(", ");
28776                self.generate_expression(position)?;
28777            }
28778            self.write(")");
28779        } else {
28780            self.write_keyword("STRPOS");
28781            self.write("(");
28782            self.generate_expression(&e.this)?;
28783            if let Some(substr) = &e.substr {
28784                self.write(", ");
28785                self.generate_expression(substr)?;
28786            }
28787            if let Some(position) = &e.position {
28788                self.write(", ");
28789                self.generate_expression(position)?;
28790            }
28791            if let Some(occurrence) = &e.occurrence {
28792                self.write(", ");
28793                self.generate_expression(occurrence)?;
28794            }
28795            self.write(")");
28796        }
28797        Ok(())
28798    }
28799
28800    fn generate_str_to_date(&mut self, e: &StrToDate) -> Result<()> {
28801        match self.config.dialect {
28802            Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => {
28803                // TO_DATE(this, java_format)
28804                self.write_keyword("TO_DATE");
28805                self.write("(");
28806                self.generate_expression(&e.this)?;
28807                if let Some(format) = &e.format {
28808                    self.write(", '");
28809                    self.write(&Self::strftime_to_java_format(format));
28810                    self.write("'");
28811                }
28812                self.write(")");
28813            }
28814            Some(DialectType::DuckDB) => {
28815                // CAST(STRPTIME(this, format) AS DATE)
28816                self.write_keyword("CAST");
28817                self.write("(");
28818                self.write_keyword("STRPTIME");
28819                self.write("(");
28820                self.generate_expression(&e.this)?;
28821                if let Some(format) = &e.format {
28822                    self.write(", '");
28823                    self.write(format);
28824                    self.write("'");
28825                }
28826                self.write(")");
28827                self.write_keyword(" AS ");
28828                self.write_keyword("DATE");
28829                self.write(")");
28830            }
28831            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
28832                // TO_DATE(this, pg_format)
28833                self.write_keyword("TO_DATE");
28834                self.write("(");
28835                self.generate_expression(&e.this)?;
28836                if let Some(format) = &e.format {
28837                    self.write(", '");
28838                    self.write(&Self::strftime_to_postgres_format(format));
28839                    self.write("'");
28840                }
28841                self.write(")");
28842            }
28843            Some(DialectType::BigQuery) => {
28844                // PARSE_DATE(format, this) - note: format comes first for BigQuery
28845                self.write_keyword("PARSE_DATE");
28846                self.write("(");
28847                if let Some(format) = &e.format {
28848                    self.write("'");
28849                    self.write(format);
28850                    self.write("'");
28851                    self.write(", ");
28852                }
28853                self.generate_expression(&e.this)?;
28854                self.write(")");
28855            }
28856            Some(DialectType::Teradata) => {
28857                // CAST(this AS DATE FORMAT 'teradata_fmt')
28858                self.write_keyword("CAST");
28859                self.write("(");
28860                self.generate_expression(&e.this)?;
28861                self.write_keyword(" AS ");
28862                self.write_keyword("DATE");
28863                if let Some(format) = &e.format {
28864                    self.write_keyword(" FORMAT ");
28865                    self.write("'");
28866                    self.write(&Self::strftime_to_teradata_format(format));
28867                    self.write("'");
28868                }
28869                self.write(")");
28870            }
28871            _ => {
28872                // STR_TO_DATE(this, format) - MySQL default
28873                self.write_keyword("STR_TO_DATE");
28874                self.write("(");
28875                self.generate_expression(&e.this)?;
28876                if let Some(format) = &e.format {
28877                    self.write(", '");
28878                    self.write(format);
28879                    self.write("'");
28880                }
28881                self.write(")");
28882            }
28883        }
28884        Ok(())
28885    }
28886
28887    /// Convert strftime format to Teradata date format (YYYY, DD, MM, etc.)
28888    fn strftime_to_teradata_format(fmt: &str) -> String {
28889        let mut result = fmt.to_string();
28890        result = result.replace("%Y", "YYYY");
28891        result = result.replace("%y", "YY");
28892        result = result.replace("%m", "MM");
28893        result = result.replace("%B", "MMMM");
28894        result = result.replace("%b", "MMM");
28895        result = result.replace("%d", "DD");
28896        result = result.replace("%j", "DDD");
28897        result = result.replace("%H", "HH");
28898        result = result.replace("%M", "MI");
28899        result = result.replace("%S", "SS");
28900        result = result.replace("%f", "SSSSSS");
28901        result = result.replace("%A", "EEEE");
28902        result = result.replace("%a", "EEE");
28903        result
28904    }
28905
28906    /// Convert strftime format (%Y, %m, %d, etc.) to Java date format (yyyy, MM, dd, etc.)
28907    fn strftime_to_java_format(fmt: &str) -> String {
28908        let mut result = fmt.to_string();
28909        result = result.replace("%Y", "yyyy");
28910        result = result.replace("%y", "yy");
28911        result = result.replace("%m", "MM");
28912        result = result.replace("%B", "MMMM");
28913        result = result.replace("%b", "MMM");
28914        result = result.replace("%d", "dd");
28915        result = result.replace("%j", "DDD");
28916        result = result.replace("%H", "HH");
28917        result = result.replace("%M", "mm");
28918        result = result.replace("%S", "ss");
28919        result = result.replace("%f", "SSSSSS");
28920        result = result.replace("%A", "EEEE");
28921        result = result.replace("%a", "EEE");
28922        result
28923    }
28924
28925    /// Convert strftime format to PostgreSQL date format (YYYY, MM, DD, etc.)
28926    fn strftime_to_postgres_format(fmt: &str) -> String {
28927        let mut result = fmt.to_string();
28928        result = result.replace("%Y", "YYYY");
28929        result = result.replace("%y", "YY");
28930        result = result.replace("%m", "MM");
28931        result = result.replace("%B", "Month");
28932        result = result.replace("%b", "Mon");
28933        result = result.replace("%d", "DD");
28934        result = result.replace("%j", "DDD");
28935        result = result.replace("%H", "HH24");
28936        result = result.replace("%M", "MI");
28937        result = result.replace("%S", "SS");
28938        result = result.replace("%f", "US");
28939        result = result.replace("%A", "Day");
28940        result = result.replace("%a", "Dy");
28941        result
28942    }
28943
28944    fn generate_str_to_map(&mut self, e: &StrToMap) -> Result<()> {
28945        // STR_TO_MAP(this, pair_delim, key_value_delim)
28946        self.write_keyword("STR_TO_MAP");
28947        self.write("(");
28948        self.generate_expression(&e.this)?;
28949        // Spark/Hive: STR_TO_MAP needs explicit default delimiters
28950        let needs_defaults = matches!(
28951            self.config.dialect,
28952            Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)
28953        );
28954        if let Some(pair_delim) = &e.pair_delim {
28955            self.write(", ");
28956            self.generate_expression(pair_delim)?;
28957        } else if needs_defaults {
28958            self.write(", ','");
28959        }
28960        if let Some(key_value_delim) = &e.key_value_delim {
28961            self.write(", ");
28962            self.generate_expression(key_value_delim)?;
28963        } else if needs_defaults {
28964            self.write(", ':'");
28965        }
28966        self.write(")");
28967        Ok(())
28968    }
28969
28970    fn generate_str_to_time(&mut self, e: &StrToTime) -> Result<()> {
28971        match self.config.dialect {
28972            Some(DialectType::Exasol) => {
28973                self.write_keyword("TO_DATE");
28974                self.write("(");
28975                self.generate_expression(&e.this)?;
28976                self.write(", '");
28977                self.write(&Self::convert_strptime_to_exasol_format(&e.format));
28978                self.write("'");
28979                self.write(")");
28980            }
28981            Some(DialectType::BigQuery) => {
28982                // BigQuery: PARSE_TIMESTAMP(format, value) - note swapped args
28983                let fmt = Self::snowflake_format_to_strftime(&e.format);
28984                // BigQuery normalizes: %Y-%m-%d -> %F, %H:%M:%S -> %T
28985                let fmt = fmt.replace("%Y-%m-%d", "%F").replace("%H:%M:%S", "%T");
28986                self.write_keyword("PARSE_TIMESTAMP");
28987                self.write("('");
28988                self.write(&fmt);
28989                self.write("', ");
28990                self.generate_expression(&e.this)?;
28991                self.write(")");
28992            }
28993            Some(DialectType::Spark) | Some(DialectType::Databricks) => {
28994                // Spark: TO_TIMESTAMP(value, format)
28995                let fmt = Self::snowflake_format_to_spark(&e.format);
28996                self.write_keyword("TO_TIMESTAMP");
28997                self.write("(");
28998                self.generate_expression(&e.this)?;
28999                self.write(", '");
29000                self.write(&fmt);
29001                self.write("')");
29002            }
29003            Some(DialectType::Presto) | Some(DialectType::Trino) => {
29004                // Presto: DATE_PARSE(value, format)
29005                let fmt = Self::snowflake_format_to_strftime(&e.format);
29006                self.write_keyword("DATE_PARSE");
29007                self.write("(");
29008                self.generate_expression(&e.this)?;
29009                self.write(", '");
29010                self.write(&fmt);
29011                self.write("')");
29012            }
29013            Some(DialectType::DuckDB) => {
29014                // DuckDB: STRPTIME(value, format)
29015                let fmt = Self::snowflake_format_to_strftime(&e.format);
29016                self.write_keyword("STRPTIME");
29017                self.write("(");
29018                self.generate_expression(&e.this)?;
29019                self.write(", '");
29020                self.write(&fmt);
29021                self.write("')");
29022            }
29023            Some(DialectType::Snowflake) => {
29024                // Snowflake: TO_TIMESTAMP(value, format)
29025                self.write_keyword("TO_TIMESTAMP");
29026                self.write("(");
29027                self.generate_expression(&e.this)?;
29028                self.write(", '");
29029                self.write(&e.format);
29030                self.write("')");
29031            }
29032            _ => {
29033                // Default: STR_TO_TIME(this, format)
29034                self.write_keyword("STR_TO_TIME");
29035                self.write("(");
29036                self.generate_expression(&e.this)?;
29037                self.write(", '");
29038                self.write(&e.format);
29039                self.write("'");
29040                self.write(")");
29041            }
29042        }
29043        Ok(())
29044    }
29045
29046    /// Convert Snowflake normalized format to strftime-style (%Y, %m, etc.)
29047    fn snowflake_format_to_strftime(format: &str) -> String {
29048        let mut result = String::new();
29049        let chars: Vec<char> = format.chars().collect();
29050        let mut i = 0;
29051        while i < chars.len() {
29052            let remaining = &format[i..];
29053            if remaining.starts_with("yyyy") {
29054                result.push_str("%Y");
29055                i += 4;
29056            } else if remaining.starts_with("yy") {
29057                result.push_str("%y");
29058                i += 2;
29059            } else if remaining.starts_with("mmmm") {
29060                result.push_str("%B"); // full month name
29061                i += 4;
29062            } else if remaining.starts_with("mon") {
29063                result.push_str("%b"); // abbreviated month
29064                i += 3;
29065            } else if remaining.starts_with("mm") {
29066                result.push_str("%m");
29067                i += 2;
29068            } else if remaining.starts_with("DD") {
29069                result.push_str("%d");
29070                i += 2;
29071            } else if remaining.starts_with("dy") {
29072                result.push_str("%a"); // abbreviated day name
29073                i += 2;
29074            } else if remaining.starts_with("hh24") {
29075                result.push_str("%H");
29076                i += 4;
29077            } else if remaining.starts_with("hh12") {
29078                result.push_str("%I");
29079                i += 4;
29080            } else if remaining.starts_with("hh") {
29081                result.push_str("%H");
29082                i += 2;
29083            } else if remaining.starts_with("mi") {
29084                result.push_str("%M");
29085                i += 2;
29086            } else if remaining.starts_with("ss") {
29087                result.push_str("%S");
29088                i += 2;
29089            } else if remaining.starts_with("ff") {
29090                // Fractional seconds
29091                result.push_str("%f");
29092                i += 2;
29093                // Skip digits after ff (ff3, ff6, ff9)
29094                while i < chars.len() && chars[i].is_ascii_digit() {
29095                    i += 1;
29096                }
29097            } else if remaining.starts_with("am") || remaining.starts_with("pm") {
29098                result.push_str("%p");
29099                i += 2;
29100            } else if remaining.starts_with("tz") {
29101                result.push_str("%Z");
29102                i += 2;
29103            } else {
29104                result.push(chars[i]);
29105                i += 1;
29106            }
29107        }
29108        result
29109    }
29110
29111    /// Convert Snowflake normalized format to Spark format (Java-style)
29112    fn snowflake_format_to_spark(format: &str) -> String {
29113        let mut result = String::new();
29114        let chars: Vec<char> = format.chars().collect();
29115        let mut i = 0;
29116        while i < chars.len() {
29117            let remaining = &format[i..];
29118            if remaining.starts_with("yyyy") {
29119                result.push_str("yyyy");
29120                i += 4;
29121            } else if remaining.starts_with("yy") {
29122                result.push_str("yy");
29123                i += 2;
29124            } else if remaining.starts_with("mmmm") {
29125                result.push_str("MMMM"); // full month name
29126                i += 4;
29127            } else if remaining.starts_with("mon") {
29128                result.push_str("MMM"); // abbreviated month
29129                i += 3;
29130            } else if remaining.starts_with("mm") {
29131                result.push_str("MM");
29132                i += 2;
29133            } else if remaining.starts_with("DD") {
29134                result.push_str("dd");
29135                i += 2;
29136            } else if remaining.starts_with("dy") {
29137                result.push_str("EEE"); // abbreviated day name
29138                i += 2;
29139            } else if remaining.starts_with("hh24") {
29140                result.push_str("HH");
29141                i += 4;
29142            } else if remaining.starts_with("hh12") {
29143                result.push_str("hh");
29144                i += 4;
29145            } else if remaining.starts_with("hh") {
29146                result.push_str("HH");
29147                i += 2;
29148            } else if remaining.starts_with("mi") {
29149                result.push_str("mm");
29150                i += 2;
29151            } else if remaining.starts_with("ss") {
29152                result.push_str("ss");
29153                i += 2;
29154            } else if remaining.starts_with("ff") {
29155                result.push_str("SSS"); // milliseconds
29156                i += 2;
29157                // Skip digits after ff
29158                while i < chars.len() && chars[i].is_ascii_digit() {
29159                    i += 1;
29160                }
29161            } else if remaining.starts_with("am") || remaining.starts_with("pm") {
29162                result.push_str("a");
29163                i += 2;
29164            } else if remaining.starts_with("tz") {
29165                result.push_str("z");
29166                i += 2;
29167            } else {
29168                result.push(chars[i]);
29169                i += 1;
29170            }
29171        }
29172        result
29173    }
29174
29175    fn generate_str_to_unix(&mut self, e: &StrToUnix) -> Result<()> {
29176        // STR_TO_UNIX(this, format)
29177        self.write_keyword("STR_TO_UNIX");
29178        self.write("(");
29179        if let Some(this) = &e.this {
29180            self.generate_expression(this)?;
29181        }
29182        if let Some(format) = &e.format {
29183            self.write(", '");
29184            self.write(format);
29185            self.write("'");
29186        }
29187        self.write(")");
29188        Ok(())
29189    }
29190
29191    fn generate_string_to_array(&mut self, e: &StringToArray) -> Result<()> {
29192        // STRING_TO_ARRAY(this, delimiter, null_string)
29193        self.write_keyword("STRING_TO_ARRAY");
29194        self.write("(");
29195        self.generate_expression(&e.this)?;
29196        if let Some(expression) = &e.expression {
29197            self.write(", ");
29198            self.generate_expression(expression)?;
29199        }
29200        if let Some(null_val) = &e.null {
29201            self.write(", ");
29202            self.generate_expression(null_val)?;
29203        }
29204        self.write(")");
29205        Ok(())
29206    }
29207
29208    fn generate_struct(&mut self, e: &Struct) -> Result<()> {
29209        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
29210            // Snowflake: OBJECT_CONSTRUCT('key', value, 'key', value, ...)
29211            self.write_keyword("OBJECT_CONSTRUCT");
29212            self.write("(");
29213            for (i, (name, expr)) in e.fields.iter().enumerate() {
29214                if i > 0 {
29215                    self.write(", ");
29216                }
29217                if let Some(name) = name {
29218                    self.write("'");
29219                    self.write(name);
29220                    self.write("'");
29221                    self.write(", ");
29222                } else {
29223                    self.write("'_");
29224                    self.write(&i.to_string());
29225                    self.write("'");
29226                    self.write(", ");
29227                }
29228                self.generate_expression(expr)?;
29229            }
29230            self.write(")");
29231        } else if self.config.struct_curly_brace_notation {
29232            // DuckDB-style: {'key': value, ...}
29233            self.write("{");
29234            for (i, (name, expr)) in e.fields.iter().enumerate() {
29235                if i > 0 {
29236                    self.write(", ");
29237                }
29238                if let Some(name) = name {
29239                    // Quote the key as a string literal
29240                    self.write("'");
29241                    self.write(name);
29242                    self.write("'");
29243                    self.write(": ");
29244                } else {
29245                    // Unnamed field: use positional key
29246                    self.write("'_");
29247                    self.write(&i.to_string());
29248                    self.write("'");
29249                    self.write(": ");
29250                }
29251                self.generate_expression(expr)?;
29252            }
29253            self.write("}");
29254        } else {
29255            // Standard SQL struct notation
29256            // BigQuery/Spark/Databricks use: STRUCT(value AS name, ...)
29257            // Others (Presto etc.) use: STRUCT(name AS value, ...) or ROW(value, ...)
29258            let value_as_name = matches!(
29259                self.config.dialect,
29260                Some(DialectType::BigQuery)
29261                | Some(DialectType::Spark)
29262               
29263                | Some(DialectType::Databricks)
29264                | Some(DialectType::Hive)
29265            );
29266            self.write_keyword("STRUCT");
29267            self.write("(");
29268            for (i, (name, expr)) in e.fields.iter().enumerate() {
29269                if i > 0 {
29270                    self.write(", ");
29271                }
29272                if let Some(name) = name {
29273                    if value_as_name {
29274                        // STRUCT(value AS name)
29275                        self.generate_expression(expr)?;
29276                        self.write_space();
29277                        self.write_keyword("AS");
29278                        self.write_space();
29279                        // Quote name if it contains spaces or special chars
29280                        let needs_quoting = name.contains(' ') || name.contains('-');
29281                        if needs_quoting {
29282                            if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive)) {
29283                                self.write("`");
29284                                self.write(name);
29285                                self.write("`");
29286                            } else {
29287                                self.write(name);
29288                            }
29289                        } else {
29290                            self.write(name);
29291                        }
29292                    } else {
29293                        // STRUCT(name AS value)
29294                        self.write(name);
29295                        self.write_space();
29296                        self.write_keyword("AS");
29297                        self.write_space();
29298                        self.generate_expression(expr)?;
29299                    }
29300                } else {
29301                    self.generate_expression(expr)?;
29302                }
29303            }
29304            self.write(")");
29305        }
29306        Ok(())
29307    }
29308
29309    fn generate_stuff(&mut self, e: &Stuff) -> Result<()> {
29310        // STUFF(this, start, length, expression)
29311        self.write_keyword("STUFF");
29312        self.write("(");
29313        self.generate_expression(&e.this)?;
29314        if let Some(start) = &e.start {
29315            self.write(", ");
29316            self.generate_expression(start)?;
29317        }
29318        if let Some(length) = e.length {
29319            self.write(", ");
29320            self.write(&length.to_string());
29321        }
29322        self.write(", ");
29323        self.generate_expression(&e.expression)?;
29324        self.write(")");
29325        Ok(())
29326    }
29327
29328    fn generate_substring_index(&mut self, e: &SubstringIndex) -> Result<()> {
29329        // SUBSTRING_INDEX(this, delimiter, count)
29330        self.write_keyword("SUBSTRING_INDEX");
29331        self.write("(");
29332        self.generate_expression(&e.this)?;
29333        if let Some(delimiter) = &e.delimiter {
29334            self.write(", ");
29335            self.generate_expression(delimiter)?;
29336        }
29337        if let Some(count) = &e.count {
29338            self.write(", ");
29339            self.generate_expression(count)?;
29340        }
29341        self.write(")");
29342        Ok(())
29343    }
29344
29345    fn generate_summarize(&mut self, e: &Summarize) -> Result<()> {
29346        // SUMMARIZE [TABLE] this
29347        self.write_keyword("SUMMARIZE");
29348        if e.table.is_some() {
29349            self.write_space();
29350            self.write_keyword("TABLE");
29351        }
29352        self.write_space();
29353        self.generate_expression(&e.this)?;
29354        Ok(())
29355    }
29356
29357    fn generate_systimestamp(&mut self, _e: &Systimestamp) -> Result<()> {
29358        // SYSTIMESTAMP
29359        self.write_keyword("SYSTIMESTAMP");
29360        Ok(())
29361    }
29362
29363    fn generate_table_alias(&mut self, e: &TableAlias) -> Result<()> {
29364        // alias (columns...)
29365        if let Some(this) = &e.this {
29366            self.generate_expression(this)?;
29367        }
29368        if !e.columns.is_empty() {
29369            self.write("(");
29370            for (i, col) in e.columns.iter().enumerate() {
29371                if i > 0 {
29372                    self.write(", ");
29373                }
29374                self.generate_expression(col)?;
29375            }
29376            self.write(")");
29377        }
29378        Ok(())
29379    }
29380
29381    fn generate_table_from_rows(&mut self, e: &TableFromRows) -> Result<()> {
29382        // TABLE(this) [AS alias]
29383        self.write_keyword("TABLE");
29384        self.write("(");
29385        self.generate_expression(&e.this)?;
29386        self.write(")");
29387        if let Some(alias) = &e.alias {
29388            self.write_space();
29389            self.write_keyword("AS");
29390            self.write_space();
29391            self.write(alias);
29392        }
29393        Ok(())
29394    }
29395
29396    fn generate_rows_from(&mut self, e: &RowsFrom) -> Result<()> {
29397        // ROWS FROM (func1(...) AS alias1(...), func2(...) AS alias2(...)) [WITH ORDINALITY] [AS alias(...)]
29398        self.write_keyword("ROWS FROM");
29399        self.write(" (");
29400        for (i, expr) in e.expressions.iter().enumerate() {
29401            if i > 0 {
29402                self.write(", ");
29403            }
29404            // Each expression is either:
29405            // - A plain function (no alias)
29406            // - A Tuple(function, TableAlias) for: FUNC() AS alias(col type, ...)
29407            match expr {
29408                Expression::Tuple(tuple) if tuple.expressions.len() == 2 => {
29409                    // First element is the function, second is the TableAlias
29410                    self.generate_expression(&tuple.expressions[0])?;
29411                    self.write_space();
29412                    self.write_keyword("AS");
29413                    self.write_space();
29414                    self.generate_expression(&tuple.expressions[1])?;
29415                }
29416                _ => {
29417                    self.generate_expression(expr)?;
29418                }
29419            }
29420        }
29421        self.write(")");
29422        if e.ordinality {
29423            self.write_space();
29424            self.write_keyword("WITH ORDINALITY");
29425        }
29426        if let Some(alias) = &e.alias {
29427            self.write_space();
29428            self.write_keyword("AS");
29429            self.write_space();
29430            self.generate_expression(alias)?;
29431        }
29432        Ok(())
29433    }
29434
29435    fn generate_table_sample(&mut self, e: &TableSample) -> Result<()> {
29436        use crate::dialects::DialectType;
29437
29438        // New wrapper pattern: expression + Sample struct
29439        if let (Some(this), Some(sample)) = (&e.this, &e.sample) {
29440            // For alias_post_tablesample dialects (Spark, Hive, Oracle): output base expr, TABLESAMPLE, then alias
29441            if self.config.alias_post_tablesample {
29442                // Handle Subquery with alias and Alias wrapper
29443                if let Expression::Subquery(ref s) = **this {
29444                    if let Some(ref alias) = s.alias {
29445                        // Create a clone without alias for output
29446                        let mut subquery_no_alias = (**s).clone();
29447                        subquery_no_alias.alias = None;
29448                        subquery_no_alias.column_aliases = Vec::new();
29449                        self.generate_expression(&Expression::Subquery(Box::new(subquery_no_alias)))?;
29450                        self.write_space();
29451                        self.write_keyword("TABLESAMPLE");
29452                        self.generate_sample_body(sample)?;
29453                        if let Some(ref seed) = sample.seed {
29454                            self.write_space();
29455                            let use_seed = sample.use_seed_keyword
29456                                && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29457                            if use_seed { self.write_keyword("SEED"); } else { self.write_keyword("REPEATABLE"); }
29458                            self.write(" (");
29459                            self.generate_expression(seed)?;
29460                            self.write(")");
29461                        }
29462                        self.write_space();
29463                        self.write_keyword("AS");
29464                        self.write_space();
29465                        self.generate_identifier(alias)?;
29466                        return Ok(());
29467                    }
29468                } else if let Expression::Alias(ref a) = **this {
29469                    // Output the base expression without alias
29470                    self.generate_expression(&a.this)?;
29471                    self.write_space();
29472                    self.write_keyword("TABLESAMPLE");
29473                    self.generate_sample_body(sample)?;
29474                    if let Some(ref seed) = sample.seed {
29475                        self.write_space();
29476                        let use_seed = sample.use_seed_keyword
29477                            && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29478                        if use_seed { self.write_keyword("SEED"); } else { self.write_keyword("REPEATABLE"); }
29479                        self.write(" (");
29480                        self.generate_expression(seed)?;
29481                        self.write(")");
29482                    }
29483                    // Output alias after TABLESAMPLE
29484                    self.write_space();
29485                    self.write_keyword("AS");
29486                    self.write_space();
29487                    self.generate_identifier(&a.alias)?;
29488                    return Ok(());
29489                }
29490            }
29491            // Default: generate wrapped expression first, then TABLESAMPLE
29492            self.generate_expression(this)?;
29493            self.write_space();
29494            self.write_keyword("TABLESAMPLE");
29495            self.generate_sample_body(sample)?;
29496            // Seed for table-level sample
29497            if let Some(ref seed) = sample.seed {
29498                self.write_space();
29499                // Databricks uses REPEATABLE, not SEED
29500                let use_seed = sample.use_seed_keyword
29501                    && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29502                if use_seed {
29503                    self.write_keyword("SEED");
29504                } else {
29505                    self.write_keyword("REPEATABLE");
29506                }
29507                self.write(" (");
29508                self.generate_expression(seed)?;
29509                self.write(")");
29510            }
29511            return Ok(());
29512        }
29513
29514        // Legacy pattern: TABLESAMPLE [method] (expressions) or TABLESAMPLE method BUCKET numerator OUT OF denominator
29515        self.write_keyword("TABLESAMPLE");
29516        if let Some(method) = &e.method {
29517            self.write_space();
29518            self.write_keyword(method);
29519        } else if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
29520            // Snowflake defaults to BERNOULLI when no method is specified
29521            self.write_space();
29522            self.write_keyword("BERNOULLI");
29523        }
29524        if let (Some(numerator), Some(denominator)) = (&e.bucket_numerator, &e.bucket_denominator) {
29525            self.write_space();
29526            self.write_keyword("BUCKET");
29527            self.write_space();
29528            self.generate_expression(numerator)?;
29529            self.write_space();
29530            self.write_keyword("OUT OF");
29531            self.write_space();
29532            self.generate_expression(denominator)?;
29533            if let Some(field) = &e.bucket_field {
29534                self.write_space();
29535                self.write_keyword("ON");
29536                self.write_space();
29537                self.generate_expression(field)?;
29538            }
29539        } else if !e.expressions.is_empty() {
29540            self.write(" (");
29541            for (i, expr) in e.expressions.iter().enumerate() {
29542                if i > 0 {
29543                    self.write(", ");
29544                }
29545                self.generate_expression(expr)?;
29546            }
29547            self.write(")");
29548        } else if let Some(percent) = &e.percent {
29549            self.write(" (");
29550            self.generate_expression(percent)?;
29551            self.write_space();
29552            self.write_keyword("PERCENT");
29553            self.write(")");
29554        }
29555        Ok(())
29556    }
29557
29558    fn generate_tag(&mut self, e: &Tag) -> Result<()> {
29559        // [prefix]this[postfix]
29560        if let Some(prefix) = &e.prefix {
29561            self.generate_expression(prefix)?;
29562        }
29563        if let Some(this) = &e.this {
29564            self.generate_expression(this)?;
29565        }
29566        if let Some(postfix) = &e.postfix {
29567            self.generate_expression(postfix)?;
29568        }
29569        Ok(())
29570    }
29571
29572    fn generate_tags(&mut self, e: &Tags) -> Result<()> {
29573        // TAG (expressions)
29574        self.write_keyword("TAG");
29575        self.write(" (");
29576        for (i, expr) in e.expressions.iter().enumerate() {
29577            if i > 0 {
29578                self.write(", ");
29579            }
29580            self.generate_expression(expr)?;
29581        }
29582        self.write(")");
29583        Ok(())
29584    }
29585
29586    fn generate_temporary_property(&mut self, e: &TemporaryProperty) -> Result<()> {
29587        // TEMPORARY or TEMP or [this] TEMPORARY
29588        if let Some(this) = &e.this {
29589            self.generate_expression(this)?;
29590            self.write_space();
29591        }
29592        self.write_keyword("TEMPORARY");
29593        Ok(())
29594    }
29595
29596    /// Generate a Time function expression
29597    /// For most dialects: TIME('value')
29598    fn generate_time_func(&mut self, e: &UnaryFunc) -> Result<()> {
29599        // Standard: TIME(value)
29600        self.write_keyword("TIME");
29601        self.write("(");
29602        self.generate_expression(&e.this)?;
29603        self.write(")");
29604        Ok(())
29605    }
29606
29607    fn generate_time_add(&mut self, e: &TimeAdd) -> Result<()> {
29608        // TIME_ADD(this, expression, unit)
29609        self.write_keyword("TIME_ADD");
29610        self.write("(");
29611        self.generate_expression(&e.this)?;
29612        self.write(", ");
29613        self.generate_expression(&e.expression)?;
29614        if let Some(unit) = &e.unit {
29615            self.write(", ");
29616            self.write_keyword(unit);
29617        }
29618        self.write(")");
29619        Ok(())
29620    }
29621
29622    fn generate_time_diff(&mut self, e: &TimeDiff) -> Result<()> {
29623        // TIME_DIFF(this, expression, unit)
29624        self.write_keyword("TIME_DIFF");
29625        self.write("(");
29626        self.generate_expression(&e.this)?;
29627        self.write(", ");
29628        self.generate_expression(&e.expression)?;
29629        if let Some(unit) = &e.unit {
29630            self.write(", ");
29631            self.write_keyword(unit);
29632        }
29633        self.write(")");
29634        Ok(())
29635    }
29636
29637    fn generate_time_from_parts(&mut self, e: &TimeFromParts) -> Result<()> {
29638        // TIME_FROM_PARTS(hour, minute, second, nanosecond)
29639        self.write_keyword("TIME_FROM_PARTS");
29640        self.write("(");
29641        let mut first = true;
29642        if let Some(hour) = &e.hour {
29643            self.generate_expression(hour)?;
29644            first = false;
29645        }
29646        if let Some(minute) = &e.min {
29647            if !first { self.write(", "); }
29648            self.generate_expression(minute)?;
29649            first = false;
29650        }
29651        if let Some(second) = &e.sec {
29652            if !first { self.write(", "); }
29653            self.generate_expression(second)?;
29654            first = false;
29655        }
29656        if let Some(ns) = &e.nano {
29657            if !first { self.write(", "); }
29658            self.generate_expression(ns)?;
29659        }
29660        self.write(")");
29661        Ok(())
29662    }
29663
29664    fn generate_time_slice(&mut self, e: &TimeSlice) -> Result<()> {
29665        // TIME_SLICE(this, expression, unit)
29666        self.write_keyword("TIME_SLICE");
29667        self.write("(");
29668        self.generate_expression(&e.this)?;
29669        self.write(", ");
29670        self.generate_expression(&e.expression)?;
29671        self.write(", ");
29672        self.write_keyword(&e.unit);
29673        self.write(")");
29674        Ok(())
29675    }
29676
29677    fn generate_time_str_to_time(&mut self, e: &TimeStrToTime) -> Result<()> {
29678        // TIME_STR_TO_TIME(this)
29679        self.write_keyword("TIME_STR_TO_TIME");
29680        self.write("(");
29681        self.generate_expression(&e.this)?;
29682        self.write(")");
29683        Ok(())
29684    }
29685
29686    fn generate_time_sub(&mut self, e: &TimeSub) -> Result<()> {
29687        // TIME_SUB(this, expression, unit)
29688        self.write_keyword("TIME_SUB");
29689        self.write("(");
29690        self.generate_expression(&e.this)?;
29691        self.write(", ");
29692        self.generate_expression(&e.expression)?;
29693        if let Some(unit) = &e.unit {
29694            self.write(", ");
29695            self.write_keyword(unit);
29696        }
29697        self.write(")");
29698        Ok(())
29699    }
29700
29701    fn generate_time_to_str(&mut self, e: &TimeToStr) -> Result<()> {
29702        // Exasol uses TO_CHAR with Exasol-specific format
29703        if self.config.dialect == Some(DialectType::Exasol) {
29704            self.write_keyword("TO_CHAR");
29705            self.write("(");
29706            self.generate_expression(&e.this)?;
29707            self.write(", '");
29708            self.write(&Self::convert_strptime_to_exasol_format(&e.format));
29709            self.write("'");
29710            self.write(")");
29711            return Ok(());
29712        }
29713
29714        // PostgreSQL/Redshift uses TO_CHAR with PG-specific format
29715        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
29716            self.write_keyword("TO_CHAR");
29717            self.write("(");
29718            self.generate_expression(&e.this)?;
29719            self.write(", '");
29720            self.write(&Self::convert_strptime_to_postgres_format(&e.format));
29721            self.write("'");
29722            self.write(")");
29723            return Ok(());
29724        }
29725
29726        // TIME_TO_STR(this, format)
29727        self.write_keyword("TIME_TO_STR");
29728        self.write("(");
29729        self.generate_expression(&e.this)?;
29730        self.write(", '");
29731        self.write(&e.format);
29732        self.write("'");
29733        self.write(")");
29734        Ok(())
29735    }
29736
29737    fn generate_time_trunc(&mut self, e: &TimeTrunc) -> Result<()> {
29738        // TIME_TRUNC(this, unit)
29739        self.write_keyword("TIME_TRUNC");
29740        self.write("(");
29741        self.generate_expression(&e.this)?;
29742        self.write(", ");
29743        self.write_keyword(&e.unit);
29744        self.write(")");
29745        Ok(())
29746    }
29747
29748    fn generate_time_unit(&mut self, e: &TimeUnit) -> Result<()> {
29749        // Just output the unit name
29750        if let Some(unit) = &e.unit {
29751            self.write_keyword(unit);
29752        }
29753        Ok(())
29754    }
29755
29756    /// Generate a Timestamp function expression
29757    /// For Exasol: {ts'value'} -> TO_TIMESTAMP('value')
29758    /// For other dialects: TIMESTAMP('value')
29759    fn generate_timestamp_func(&mut self, e: &TimestampFunc) -> Result<()> {
29760        use crate::dialects::DialectType;
29761        use crate::expressions::Literal;
29762
29763        match self.config.dialect {
29764            // Exasol uses TO_TIMESTAMP for Timestamp expressions
29765            Some(DialectType::Exasol) => {
29766                self.write_keyword("TO_TIMESTAMP");
29767                self.write("(");
29768                // Extract the string value from the expression if it's a string literal
29769                if let Some(this) = &e.this {
29770                    match this.as_ref() {
29771                        Expression::Literal(Literal::String(s)) => {
29772                            self.write("'");
29773                            self.write(s);
29774                            self.write("'");
29775                        }
29776                        _ => {
29777                            self.generate_expression(this)?;
29778                        }
29779                    }
29780                }
29781                self.write(")");
29782            }
29783            // Standard: TIMESTAMP(value) or TIMESTAMP(value, zone)
29784            _ => {
29785                self.write_keyword("TIMESTAMP");
29786                self.write("(");
29787                if let Some(this) = &e.this {
29788                    self.generate_expression(this)?;
29789                }
29790                if let Some(zone) = &e.zone {
29791                    self.write(", ");
29792                    self.generate_expression(zone)?;
29793                }
29794                self.write(")");
29795            }
29796        }
29797        Ok(())
29798    }
29799
29800    fn generate_timestamp_add(&mut self, e: &TimestampAdd) -> Result<()> {
29801        // TIMESTAMP_ADD(this, expression, unit)
29802        self.write_keyword("TIMESTAMP_ADD");
29803        self.write("(");
29804        self.generate_expression(&e.this)?;
29805        self.write(", ");
29806        self.generate_expression(&e.expression)?;
29807        if let Some(unit) = &e.unit {
29808            self.write(", ");
29809            self.write_keyword(unit);
29810        }
29811        self.write(")");
29812        Ok(())
29813    }
29814
29815    fn generate_timestamp_diff(&mut self, e: &TimestampDiff) -> Result<()> {
29816        // TIMESTAMP_DIFF(this, expression, unit)
29817        self.write_keyword("TIMESTAMP_DIFF");
29818        self.write("(");
29819        self.generate_expression(&e.this)?;
29820        self.write(", ");
29821        self.generate_expression(&e.expression)?;
29822        if let Some(unit) = &e.unit {
29823            self.write(", ");
29824            self.write_keyword(unit);
29825        }
29826        self.write(")");
29827        Ok(())
29828    }
29829
29830    fn generate_timestamp_from_parts(&mut self, e: &TimestampFromParts) -> Result<()> {
29831        // TIMESTAMP_FROM_PARTS(this, expression)
29832        self.write_keyword("TIMESTAMP_FROM_PARTS");
29833        self.write("(");
29834        if let Some(this) = &e.this {
29835            self.generate_expression(this)?;
29836        }
29837        if let Some(expression) = &e.expression {
29838            self.write(", ");
29839            self.generate_expression(expression)?;
29840        }
29841        if let Some(zone) = &e.zone {
29842            self.write(", ");
29843            self.generate_expression(zone)?;
29844        }
29845        if let Some(milli) = &e.milli {
29846            self.write(", ");
29847            self.generate_expression(milli)?;
29848        }
29849        self.write(")");
29850        Ok(())
29851    }
29852
29853    fn generate_timestamp_sub(&mut self, e: &TimestampSub) -> Result<()> {
29854        // TIMESTAMP_SUB(this, INTERVAL expression unit)
29855        self.write_keyword("TIMESTAMP_SUB");
29856        self.write("(");
29857        self.generate_expression(&e.this)?;
29858        self.write(", ");
29859        self.write_keyword("INTERVAL");
29860        self.write_space();
29861        self.generate_expression(&e.expression)?;
29862        if let Some(unit) = &e.unit {
29863            self.write_space();
29864            self.write_keyword(unit);
29865        }
29866        self.write(")");
29867        Ok(())
29868    }
29869
29870    fn generate_timestamp_tz_from_parts(&mut self, e: &TimestampTzFromParts) -> Result<()> {
29871        // TIMESTAMP_TZ_FROM_PARTS(...)
29872        self.write_keyword("TIMESTAMP_TZ_FROM_PARTS");
29873        self.write("(");
29874        if let Some(zone) = &e.zone {
29875            self.generate_expression(zone)?;
29876        }
29877        self.write(")");
29878        Ok(())
29879    }
29880
29881    fn generate_to_binary(&mut self, e: &ToBinary) -> Result<()> {
29882        // TO_BINARY(this, [format])
29883        self.write_keyword("TO_BINARY");
29884        self.write("(");
29885        self.generate_expression(&e.this)?;
29886        if let Some(format) = &e.format {
29887            self.write(", '");
29888            self.write(format);
29889            self.write("'");
29890        }
29891        self.write(")");
29892        Ok(())
29893    }
29894
29895    fn generate_to_boolean(&mut self, e: &ToBoolean) -> Result<()> {
29896        // TO_BOOLEAN(this)
29897        self.write_keyword("TO_BOOLEAN");
29898        self.write("(");
29899        self.generate_expression(&e.this)?;
29900        self.write(")");
29901        Ok(())
29902    }
29903
29904    fn generate_to_char(&mut self, e: &ToChar) -> Result<()> {
29905        // TO_CHAR(this, [format], [nlsparam])
29906        self.write_keyword("TO_CHAR");
29907        self.write("(");
29908        self.generate_expression(&e.this)?;
29909        if let Some(format) = &e.format {
29910            self.write(", '");
29911            self.write(format);
29912            self.write("'");
29913        }
29914        if let Some(nlsparam) = &e.nlsparam {
29915            self.write(", ");
29916            self.generate_expression(nlsparam)?;
29917        }
29918        self.write(")");
29919        Ok(())
29920    }
29921
29922    fn generate_to_decfloat(&mut self, e: &ToDecfloat) -> Result<()> {
29923        // TO_DECFLOAT(this, [format])
29924        self.write_keyword("TO_DECFLOAT");
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        self.write(")");
29933        Ok(())
29934    }
29935
29936    fn generate_to_double(&mut self, e: &ToDouble) -> Result<()> {
29937        // TO_DOUBLE(this, [format])
29938        self.write_keyword("TO_DOUBLE");
29939        self.write("(");
29940        self.generate_expression(&e.this)?;
29941        if let Some(format) = &e.format {
29942            self.write(", '");
29943            self.write(format);
29944            self.write("'");
29945        }
29946        self.write(")");
29947        Ok(())
29948    }
29949
29950    fn generate_to_file(&mut self, e: &ToFile) -> Result<()> {
29951        // TO_FILE(this, path)
29952        self.write_keyword("TO_FILE");
29953        self.write("(");
29954        self.generate_expression(&e.this)?;
29955        if let Some(path) = &e.path {
29956            self.write(", ");
29957            self.generate_expression(path)?;
29958        }
29959        self.write(")");
29960        Ok(())
29961    }
29962
29963    fn generate_to_number(&mut self, e: &ToNumber) -> Result<()> {
29964        // TO_NUMBER or TRY_TO_NUMBER (this, [format], [precision], [scale])
29965        // If safe flag is set, output TRY_TO_NUMBER
29966        let is_safe = e.safe.is_some();
29967        if is_safe {
29968            self.write_keyword("TRY_TO_NUMBER");
29969        } else {
29970            self.write_keyword("TO_NUMBER");
29971        }
29972        self.write("(");
29973        self.generate_expression(&e.this)?;
29974        if let Some(format) = &e.format {
29975            self.write(", ");
29976            self.generate_expression(format)?;
29977        }
29978        if let Some(nlsparam) = &e.nlsparam {
29979            self.write(", ");
29980            self.generate_expression(nlsparam)?;
29981        }
29982        if let Some(precision) = &e.precision {
29983            self.write(", ");
29984            self.generate_expression(precision)?;
29985        }
29986        if let Some(scale) = &e.scale {
29987            self.write(", ");
29988            self.generate_expression(scale)?;
29989        }
29990        self.write(")");
29991        Ok(())
29992    }
29993
29994    fn generate_to_table_property(&mut self, e: &ToTableProperty) -> Result<()> {
29995        // TO_TABLE this
29996        self.write_keyword("TO_TABLE");
29997        self.write_space();
29998        self.generate_expression(&e.this)?;
29999        Ok(())
30000    }
30001
30002    fn generate_transaction(&mut self, e: &Transaction) -> Result<()> {
30003        // Check mark to determine the format
30004        let mark_text = e.mark.as_ref().map(|m| {
30005            match m.as_ref() {
30006                Expression::Identifier(id) => id.name.clone(),
30007                Expression::Literal(Literal::String(s)) => s.clone(),
30008                _ => String::new(),
30009            }
30010        });
30011
30012        let is_start = mark_text.as_ref().map_or(false, |s| s == "START");
30013        let has_transaction_keyword = mark_text.as_ref().map_or(false, |s| s == "TRANSACTION");
30014        let has_with_mark = e.mark.as_ref().map_or(false, |m| {
30015            matches!(m.as_ref(), Expression::Literal(Literal::String(_)))
30016        });
30017
30018        if is_start {
30019            // START TRANSACTION [modes]
30020            self.write_keyword("START TRANSACTION");
30021            if let Some(modes) = &e.modes {
30022                self.write_space();
30023                self.generate_expression(modes)?;
30024            }
30025        } else {
30026            // BEGIN [DEFERRED|IMMEDIATE|EXCLUSIVE] [TRANSACTION] [transaction_name] [WITH MARK 'desc']
30027            self.write_keyword("BEGIN");
30028
30029            // Output TRANSACTION keyword if it was present
30030            if has_transaction_keyword || has_with_mark {
30031                self.write_space();
30032                self.write_keyword("TRANSACTION");
30033            }
30034
30035            // Output transaction name or kind (e.g., DEFERRED)
30036            if let Some(this) = &e.this {
30037                self.write_space();
30038                self.generate_expression(this)?;
30039            }
30040
30041            // Output WITH MARK 'description' for TSQL
30042            if has_with_mark {
30043                self.write_space();
30044                self.write_keyword("WITH MARK");
30045                if let Some(Expression::Literal(Literal::String(desc))) = e.mark.as_deref() {
30046                    if !desc.is_empty() {
30047                        self.write_space();
30048                        self.write(&format!("'{}'", desc));
30049                    }
30050                }
30051            }
30052
30053            // Output modes (isolation levels, etc.)
30054            if let Some(modes) = &e.modes {
30055                self.write_space();
30056                self.generate_expression(modes)?;
30057            }
30058        }
30059        Ok(())
30060    }
30061
30062    fn generate_transform(&mut self, e: &Transform) -> Result<()> {
30063        // TRANSFORM(this, expression)
30064        self.write_keyword("TRANSFORM");
30065        self.write("(");
30066        self.generate_expression(&e.this)?;
30067        self.write(", ");
30068        self.generate_expression(&e.expression)?;
30069        self.write(")");
30070        Ok(())
30071    }
30072
30073    fn generate_transform_model_property(&mut self, e: &TransformModelProperty) -> Result<()> {
30074        // TRANSFORM(expressions)
30075        self.write_keyword("TRANSFORM");
30076        self.write("(");
30077        if self.config.pretty && !e.expressions.is_empty() {
30078            self.indent_level += 1;
30079            for (i, expr) in e.expressions.iter().enumerate() {
30080                if i > 0 {
30081                    self.write(",");
30082                }
30083                self.write_newline();
30084                self.write_indent();
30085                self.generate_expression(expr)?;
30086            }
30087            self.indent_level -= 1;
30088            self.write_newline();
30089            self.write(")");
30090        } else {
30091            for (i, expr) in e.expressions.iter().enumerate() {
30092                if i > 0 {
30093                    self.write(", ");
30094                }
30095                self.generate_expression(expr)?;
30096            }
30097            self.write(")");
30098        }
30099        Ok(())
30100    }
30101
30102    fn generate_transient_property(&mut self, e: &TransientProperty) -> Result<()> {
30103        use crate::dialects::DialectType;
30104        // TRANSIENT is Snowflake-specific; skip for other dialects
30105        if let Some(this) = &e.this {
30106            self.generate_expression(this)?;
30107            if matches!(self.config.dialect, Some(DialectType::Snowflake) | None) {
30108                self.write_space();
30109            }
30110        }
30111        if matches!(self.config.dialect, Some(DialectType::Snowflake) | None) {
30112            self.write_keyword("TRANSIENT");
30113        }
30114        Ok(())
30115    }
30116
30117    fn generate_translate(&mut self, e: &Translate) -> Result<()> {
30118        // TRANSLATE(this, from_, to)
30119        self.write_keyword("TRANSLATE");
30120        self.write("(");
30121        self.generate_expression(&e.this)?;
30122        if let Some(from) = &e.from_ {
30123            self.write(", ");
30124            self.generate_expression(from)?;
30125        }
30126        if let Some(to) = &e.to {
30127            self.write(", ");
30128            self.generate_expression(to)?;
30129        }
30130        self.write(")");
30131        Ok(())
30132    }
30133
30134    fn generate_translate_characters(&mut self, e: &TranslateCharacters) -> Result<()> {
30135        // TRANSLATE(this USING expression)
30136        self.write_keyword("TRANSLATE");
30137        self.write("(");
30138        self.generate_expression(&e.this)?;
30139        self.write_space();
30140        self.write_keyword("USING");
30141        self.write_space();
30142        self.generate_expression(&e.expression)?;
30143        if e.with_error.is_some() {
30144            self.write_space();
30145            self.write_keyword("WITH ERROR");
30146        }
30147        self.write(")");
30148        Ok(())
30149    }
30150
30151    fn generate_truncate_table(&mut self, e: &TruncateTable) -> Result<()> {
30152        // TRUNCATE TABLE table1, table2, ...
30153        self.write_keyword("TRUNCATE TABLE");
30154        self.write_space();
30155        for (i, expr) in e.expressions.iter().enumerate() {
30156            if i > 0 {
30157                self.write(", ");
30158            }
30159            self.generate_expression(expr)?;
30160        }
30161        Ok(())
30162    }
30163
30164    fn generate_try_base64_decode_binary(&mut self, e: &TryBase64DecodeBinary) -> Result<()> {
30165        // TRY_BASE64_DECODE_BINARY(this, [alphabet])
30166        self.write_keyword("TRY_BASE64_DECODE_BINARY");
30167        self.write("(");
30168        self.generate_expression(&e.this)?;
30169        if let Some(alphabet) = &e.alphabet {
30170            self.write(", ");
30171            self.generate_expression(alphabet)?;
30172        }
30173        self.write(")");
30174        Ok(())
30175    }
30176
30177    fn generate_try_base64_decode_string(&mut self, e: &TryBase64DecodeString) -> Result<()> {
30178        // TRY_BASE64_DECODE_STRING(this, [alphabet])
30179        self.write_keyword("TRY_BASE64_DECODE_STRING");
30180        self.write("(");
30181        self.generate_expression(&e.this)?;
30182        if let Some(alphabet) = &e.alphabet {
30183            self.write(", ");
30184            self.generate_expression(alphabet)?;
30185        }
30186        self.write(")");
30187        Ok(())
30188    }
30189
30190    fn generate_try_to_decfloat(&mut self, e: &TryToDecfloat) -> Result<()> {
30191        // TRY_TO_DECFLOAT(this, [format])
30192        self.write_keyword("TRY_TO_DECFLOAT");
30193        self.write("(");
30194        self.generate_expression(&e.this)?;
30195        if let Some(format) = &e.format {
30196            self.write(", '");
30197            self.write(format);
30198            self.write("'");
30199        }
30200        self.write(")");
30201        Ok(())
30202    }
30203
30204    fn generate_ts_or_ds_add(&mut self, e: &TsOrDsAdd) -> Result<()> {
30205        // TS_OR_DS_ADD(this, expression, [unit], [return_type])
30206        self.write_keyword("TS_OR_DS_ADD");
30207        self.write("(");
30208        self.generate_expression(&e.this)?;
30209        self.write(", ");
30210        self.generate_expression(&e.expression)?;
30211        if let Some(unit) = &e.unit {
30212            self.write(", ");
30213            self.write_keyword(unit);
30214        }
30215        if let Some(return_type) = &e.return_type {
30216            self.write(", ");
30217            self.generate_expression(return_type)?;
30218        }
30219        self.write(")");
30220        Ok(())
30221    }
30222
30223    fn generate_ts_or_ds_diff(&mut self, e: &TsOrDsDiff) -> Result<()> {
30224        // TS_OR_DS_DIFF(this, expression, [unit])
30225        self.write_keyword("TS_OR_DS_DIFF");
30226        self.write("(");
30227        self.generate_expression(&e.this)?;
30228        self.write(", ");
30229        self.generate_expression(&e.expression)?;
30230        if let Some(unit) = &e.unit {
30231            self.write(", ");
30232            self.write_keyword(unit);
30233        }
30234        self.write(")");
30235        Ok(())
30236    }
30237
30238    fn generate_ts_or_ds_to_date(&mut self, e: &TsOrDsToDate) -> Result<()> {
30239        // TS_OR_DS_TO_DATE(this, [format])
30240        self.write_keyword("TS_OR_DS_TO_DATE");
30241        self.write("(");
30242        self.generate_expression(&e.this)?;
30243        if let Some(format) = &e.format {
30244            self.write(", '");
30245            self.write(format);
30246            self.write("'");
30247        }
30248        self.write(")");
30249        Ok(())
30250    }
30251
30252    fn generate_ts_or_ds_to_time(&mut self, e: &TsOrDsToTime) -> Result<()> {
30253        // TS_OR_DS_TO_TIME(this, [format])
30254        self.write_keyword("TS_OR_DS_TO_TIME");
30255        self.write("(");
30256        self.generate_expression(&e.this)?;
30257        if let Some(format) = &e.format {
30258            self.write(", '");
30259            self.write(format);
30260            self.write("'");
30261        }
30262        self.write(")");
30263        Ok(())
30264    }
30265
30266    fn generate_unhex(&mut self, e: &Unhex) -> Result<()> {
30267        // UNHEX(this, [expression])
30268        self.write_keyword("UNHEX");
30269        self.write("(");
30270        self.generate_expression(&e.this)?;
30271        if let Some(expression) = &e.expression {
30272            self.write(", ");
30273            self.generate_expression(expression)?;
30274        }
30275        self.write(")");
30276        Ok(())
30277    }
30278
30279    fn generate_unicode_string(&mut self, e: &UnicodeString) -> Result<()> {
30280        // U&this [UESCAPE escape]
30281        self.write("U&");
30282        self.generate_expression(&e.this)?;
30283        if let Some(escape) = &e.escape {
30284            self.write_space();
30285            self.write_keyword("UESCAPE");
30286            self.write_space();
30287            self.generate_expression(escape)?;
30288        }
30289        Ok(())
30290    }
30291
30292    fn generate_uniform(&mut self, e: &Uniform) -> Result<()> {
30293        // UNIFORM(this, expression, [gen], [seed])
30294        self.write_keyword("UNIFORM");
30295        self.write("(");
30296        self.generate_expression(&e.this)?;
30297        self.write(", ");
30298        self.generate_expression(&e.expression)?;
30299        if let Some(gen) = &e.gen {
30300            self.write(", ");
30301            self.generate_expression(gen)?;
30302        }
30303        if let Some(seed) = &e.seed {
30304            self.write(", ");
30305            self.generate_expression(seed)?;
30306        }
30307        self.write(")");
30308        Ok(())
30309    }
30310
30311    fn generate_unique_column_constraint(&mut self, e: &UniqueColumnConstraint) -> Result<()> {
30312        // UNIQUE [NULLS NOT DISTINCT] [this] [index_type] [on_conflict] [options]
30313        self.write_keyword("UNIQUE");
30314        // Output NULLS NOT DISTINCT if nulls is set (PostgreSQL 15+ feature)
30315        if e.nulls.is_some() {
30316            self.write(" NULLS NOT DISTINCT");
30317        }
30318        if let Some(this) = &e.this {
30319            self.write_space();
30320            self.generate_expression(this)?;
30321        }
30322        if let Some(index_type) = &e.index_type {
30323            self.write(" USING ");
30324            self.generate_expression(index_type)?;
30325        }
30326        if let Some(on_conflict) = &e.on_conflict {
30327            self.write_space();
30328            self.generate_expression(on_conflict)?;
30329        }
30330        for opt in &e.options {
30331            self.write_space();
30332            self.generate_expression(opt)?;
30333        }
30334        Ok(())
30335    }
30336
30337    fn generate_unique_key_property(&mut self, e: &UniqueKeyProperty) -> Result<()> {
30338        // UNIQUE KEY (expressions)
30339        self.write_keyword("UNIQUE KEY");
30340        self.write(" (");
30341        for (i, expr) in e.expressions.iter().enumerate() {
30342            if i > 0 {
30343                self.write(", ");
30344            }
30345            self.generate_expression(expr)?;
30346        }
30347        self.write(")");
30348        Ok(())
30349    }
30350
30351    fn generate_rollup_property(&mut self, e: &RollupProperty) -> Result<()> {
30352        // ROLLUP (r1(col1, col2), r2(col1))
30353        self.write_keyword("ROLLUP");
30354        self.write(" (");
30355        for (i, index) in e.expressions.iter().enumerate() {
30356            if i > 0 {
30357                self.write(", ");
30358            }
30359            self.generate_identifier(&index.name)?;
30360            self.write("(");
30361            for (j, col) in index.expressions.iter().enumerate() {
30362                if j > 0 {
30363                    self.write(", ");
30364                }
30365                self.generate_identifier(col)?;
30366            }
30367            self.write(")");
30368        }
30369        self.write(")");
30370        Ok(())
30371    }
30372
30373    fn generate_unix_to_str(&mut self, e: &UnixToStr) -> Result<()> {
30374        // UNIX_TO_STR(this, [format])
30375        self.write_keyword("UNIX_TO_STR");
30376        self.write("(");
30377        self.generate_expression(&e.this)?;
30378        if let Some(format) = &e.format {
30379            self.write(", '");
30380            self.write(format);
30381            self.write("'");
30382        }
30383        self.write(")");
30384        Ok(())
30385    }
30386
30387    fn generate_unix_to_time(&mut self, e: &UnixToTime) -> Result<()> {
30388        use crate::dialects::DialectType;
30389        let scale = e.scale.unwrap_or(0); // 0 = seconds
30390
30391        match self.config.dialect {
30392            Some(DialectType::Snowflake) => {
30393                // Snowflake: TO_TIMESTAMP(value[, scale]) - skip scale for seconds (0)
30394                self.write_keyword("TO_TIMESTAMP");
30395                self.write("(");
30396                self.generate_expression(&e.this)?;
30397                if let Some(s) = e.scale {
30398                    if s > 0 {
30399                        self.write(", ");
30400                        self.write(&s.to_string());
30401                    }
30402                }
30403                self.write(")");
30404            }
30405            Some(DialectType::BigQuery) => {
30406                // BigQuery: TIMESTAMP_SECONDS(value) / TIMESTAMP_MILLIS(value)
30407                // or TIMESTAMP_SECONDS(CAST(value / POWER(10, scale) AS INT64)) for other scales
30408                match scale {
30409                    0 => {
30410                        self.write_keyword("TIMESTAMP_SECONDS");
30411                        self.write("(");
30412                        self.generate_expression(&e.this)?;
30413                        self.write(")");
30414                    }
30415                    3 => {
30416                        self.write_keyword("TIMESTAMP_MILLIS");
30417                        self.write("(");
30418                        self.generate_expression(&e.this)?;
30419                        self.write(")");
30420                    }
30421                    6 => {
30422                        self.write_keyword("TIMESTAMP_MICROS");
30423                        self.write("(");
30424                        self.generate_expression(&e.this)?;
30425                        self.write(")");
30426                    }
30427                    _ => {
30428                        // TIMESTAMP_SECONDS(CAST(value / POWER(10, scale) AS INT64))
30429                        self.write_keyword("TIMESTAMP_SECONDS");
30430                        self.write("(CAST(");
30431                        self.generate_expression(&e.this)?;
30432                        self.write(&format!(" / POWER(10, {}) AS INT64))", scale));
30433                    }
30434                }
30435            }
30436            Some(DialectType::Spark) => {
30437                // Spark: CAST(FROM_UNIXTIME(value) AS TIMESTAMP) for scale=0
30438                // TIMESTAMP_MILLIS(value) for scale=3
30439                // TIMESTAMP_MICROS(value) for scale=6
30440                // TIMESTAMP_SECONDS(value / POWER(10, scale)) for other scales
30441                match scale {
30442                    0 => {
30443                        self.write_keyword("CAST");
30444                        self.write("(");
30445                        self.write_keyword("FROM_UNIXTIME");
30446                        self.write("(");
30447                        self.generate_expression(&e.this)?;
30448                        self.write(") ");
30449                        self.write_keyword("AS TIMESTAMP");
30450                        self.write(")");
30451                    }
30452                    3 => {
30453                        self.write_keyword("TIMESTAMP_MILLIS");
30454                        self.write("(");
30455                        self.generate_expression(&e.this)?;
30456                        self.write(")");
30457                    }
30458                    6 => {
30459                        self.write_keyword("TIMESTAMP_MICROS");
30460                        self.write("(");
30461                        self.generate_expression(&e.this)?;
30462                        self.write(")");
30463                    }
30464                    _ => {
30465                        self.write_keyword("TIMESTAMP_SECONDS");
30466                        self.write("(");
30467                        self.generate_expression(&e.this)?;
30468                        self.write(&format!(" / POWER(10, {}))", scale));
30469                    }
30470                }
30471            }
30472            Some(DialectType::Databricks) => {
30473                // Databricks: CAST(FROM_UNIXTIME(value) AS TIMESTAMP) for scale=0
30474                // TIMESTAMP_MILLIS(value) for scale=3
30475                // TIMESTAMP_MICROS(value) for scale=6
30476                match scale {
30477                    0 => {
30478                        self.write_keyword("CAST");
30479                        self.write("(");
30480                        self.write_keyword("FROM_UNIXTIME");
30481                        self.write("(");
30482                        self.generate_expression(&e.this)?;
30483                        self.write(") ");
30484                        self.write_keyword("AS TIMESTAMP");
30485                        self.write(")");
30486                    }
30487                    3 => {
30488                        self.write_keyword("TIMESTAMP_MILLIS");
30489                        self.write("(");
30490                        self.generate_expression(&e.this)?;
30491                        self.write(")");
30492                    }
30493                    6 => {
30494                        self.write_keyword("TIMESTAMP_MICROS");
30495                        self.write("(");
30496                        self.generate_expression(&e.this)?;
30497                        self.write(")");
30498                    }
30499                    _ => {
30500                        self.write_keyword("TIMESTAMP_SECONDS");
30501                        self.write("(");
30502                        self.generate_expression(&e.this)?;
30503                        self.write(&format!(" / POWER(10, {}))", scale));
30504                    }
30505                }
30506            }
30507            Some(DialectType::Presto) | Some(DialectType::Trino) => {
30508                // Presto: FROM_UNIXTIME(CAST(value AS DOUBLE) / POW(10, scale)) for scale > 0
30509                // FROM_UNIXTIME(value) for scale=0
30510                if scale == 0 {
30511                    self.write_keyword("FROM_UNIXTIME");
30512                    self.write("(");
30513                    self.generate_expression(&e.this)?;
30514                    self.write(")");
30515                } else {
30516                    self.write_keyword("FROM_UNIXTIME");
30517                    self.write("(CAST(");
30518                    self.generate_expression(&e.this)?;
30519                    self.write(&format!(" AS DOUBLE) / POW(10, {}))", scale));
30520                }
30521            }
30522            Some(DialectType::DuckDB) => {
30523                // DuckDB: TO_TIMESTAMP(value) for scale=0
30524                // EPOCH_MS(value) for scale=3
30525                // MAKE_TIMESTAMP(value) for scale=6
30526                match scale {
30527                    0 => {
30528                        self.write_keyword("TO_TIMESTAMP");
30529                        self.write("(");
30530                        self.generate_expression(&e.this)?;
30531                        self.write(")");
30532                    }
30533                    3 => {
30534                        self.write_keyword("EPOCH_MS");
30535                        self.write("(");
30536                        self.generate_expression(&e.this)?;
30537                        self.write(")");
30538                    }
30539                    6 => {
30540                        self.write_keyword("MAKE_TIMESTAMP");
30541                        self.write("(");
30542                        self.generate_expression(&e.this)?;
30543                        self.write(")");
30544                    }
30545                    _ => {
30546                        self.write_keyword("TO_TIMESTAMP");
30547                        self.write("(");
30548                        self.generate_expression(&e.this)?;
30549                        self.write(&format!(" / POWER(10, {}))", scale));
30550                        self.write_keyword(" AT TIME ZONE");
30551                        self.write(" 'UTC'");
30552                    }
30553                }
30554            }
30555            Some(DialectType::Redshift) => {
30556                // Redshift: (TIMESTAMP 'epoch' + value * INTERVAL '1 SECOND') for scale=0
30557                // (TIMESTAMP 'epoch' + (value / POWER(10, scale)) * INTERVAL '1 SECOND') for scale > 0
30558                self.write("(TIMESTAMP 'epoch' + ");
30559                if scale == 0 {
30560                    self.generate_expression(&e.this)?;
30561                } else {
30562                    self.write("(");
30563                    self.generate_expression(&e.this)?;
30564                    self.write(&format!(" / POWER(10, {}))", scale));
30565                }
30566                self.write(" * INTERVAL '1 SECOND')");
30567            }
30568            _ => {
30569                // Default: TO_TIMESTAMP(value[, scale])
30570                self.write_keyword("TO_TIMESTAMP");
30571                self.write("(");
30572                self.generate_expression(&e.this)?;
30573                if let Some(s) = e.scale {
30574                    self.write(", ");
30575                    self.write(&s.to_string());
30576                }
30577                self.write(")");
30578            }
30579        }
30580        Ok(())
30581    }
30582
30583    fn generate_unpivot_columns(&mut self, e: &UnpivotColumns) -> Result<()> {
30584        // NAME col VALUE col1, col2, ...
30585        if !matches!(&*e.this, Expression::Null(_)) {
30586            self.write_keyword("NAME");
30587            self.write_space();
30588            self.generate_expression(&e.this)?;
30589        }
30590        if !e.expressions.is_empty() {
30591            self.write_space();
30592            self.write_keyword("VALUE");
30593            self.write_space();
30594            for (i, expr) in e.expressions.iter().enumerate() {
30595                if i > 0 {
30596                    self.write(", ");
30597                }
30598                self.generate_expression(expr)?;
30599            }
30600        }
30601        Ok(())
30602    }
30603
30604    fn generate_user_defined_function(&mut self, e: &UserDefinedFunction) -> Result<()> {
30605        // this(expressions) or (this)(expressions)
30606        if e.wrapped.is_some() {
30607            self.write("(");
30608        }
30609        self.generate_expression(&e.this)?;
30610        if e.wrapped.is_some() {
30611            self.write(")");
30612        }
30613        self.write("(");
30614        for (i, expr) in e.expressions.iter().enumerate() {
30615            if i > 0 {
30616                self.write(", ");
30617            }
30618            self.generate_expression(expr)?;
30619        }
30620        self.write(")");
30621        Ok(())
30622    }
30623
30624    fn generate_using_template_property(&mut self, e: &UsingTemplateProperty) -> Result<()> {
30625        // USING TEMPLATE this
30626        self.write_keyword("USING TEMPLATE");
30627        self.write_space();
30628        self.generate_expression(&e.this)?;
30629        Ok(())
30630    }
30631
30632    fn generate_utc_time(&mut self, _e: &UtcTime) -> Result<()> {
30633        // UTC_TIME
30634        self.write_keyword("UTC_TIME");
30635        Ok(())
30636    }
30637
30638    fn generate_utc_timestamp(&mut self, _e: &UtcTimestamp) -> Result<()> {
30639        // UTC_TIMESTAMP
30640        self.write_keyword("UTC_TIMESTAMP");
30641        Ok(())
30642    }
30643
30644    fn generate_uuid(&mut self, e: &Uuid) -> Result<()> {
30645        use crate::dialects::DialectType;
30646        // Choose UUID function name based on target dialect
30647        let func_name = match self.config.dialect {
30648            Some(DialectType::Snowflake) => "UUID_STRING",
30649            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => "GEN_RANDOM_UUID",
30650            Some(DialectType::BigQuery) => "GENERATE_UUID",
30651            _ => {
30652                if let Some(name) = &e.name {
30653                    name.as_str()
30654                } else {
30655                    "UUID"
30656                }
30657            }
30658        };
30659        self.write_keyword(func_name);
30660        self.write("(");
30661        if let Some(this) = &e.this {
30662            self.generate_expression(this)?;
30663        }
30664        self.write(")");
30665        Ok(())
30666    }
30667
30668    fn generate_var_map(&mut self, e: &VarMap) -> Result<()> {
30669        // MAP(key1, value1, key2, value2, ...)
30670        self.write_keyword("MAP");
30671        self.write("(");
30672        let mut first = true;
30673        for (k, v) in e.keys.iter().zip(e.values.iter()) {
30674            if !first {
30675                self.write(", ");
30676            }
30677            self.generate_expression(k)?;
30678            self.write(", ");
30679            self.generate_expression(v)?;
30680            first = false;
30681        }
30682        self.write(")");
30683        Ok(())
30684    }
30685
30686    fn generate_vector_search(&mut self, e: &VectorSearch) -> Result<()> {
30687        // VECTOR_SEARCH(this, column_to_search, query_table, query_column_to_search, top_k, distance_type, ...)
30688        self.write_keyword("VECTOR_SEARCH");
30689        self.write("(");
30690        self.generate_expression(&e.this)?;
30691        if let Some(col) = &e.column_to_search {
30692            self.write(", ");
30693            self.generate_expression(col)?;
30694        }
30695        if let Some(query_table) = &e.query_table {
30696            self.write(", ");
30697            self.generate_expression(query_table)?;
30698        }
30699        if let Some(query_col) = &e.query_column_to_search {
30700            self.write(", ");
30701            self.generate_expression(query_col)?;
30702        }
30703        if let Some(top_k) = &e.top_k {
30704            self.write(", ");
30705            self.generate_expression(top_k)?;
30706        }
30707        if let Some(dist_type) = &e.distance_type {
30708            self.write(", ");
30709            self.generate_expression(dist_type)?;
30710        }
30711        self.write(")");
30712        Ok(())
30713    }
30714
30715    fn generate_version(&mut self, e: &Version) -> Result<()> {
30716        // Python: f"FOR {expression.name} {kind} {expr}"
30717        // e.this = Identifier("TIMESTAMP" or "VERSION")
30718        // e.kind = "AS OF" (or "BETWEEN", etc.)
30719        // e.expression = the value expression
30720        // Hive does NOT use the FOR prefix for time travel
30721        use crate::dialects::DialectType;
30722        let skip_for = matches!(self.config.dialect, Some(DialectType::Hive) | Some(DialectType::Spark));
30723        if !skip_for {
30724            self.write_keyword("FOR");
30725            self.write_space();
30726        }
30727        // Extract the name from this (which is an Identifier expression)
30728        match e.this.as_ref() {
30729            Expression::Identifier(ident) => {
30730                self.write_keyword(&ident.name);
30731            }
30732            _ => {
30733                self.generate_expression(&e.this)?;
30734            }
30735        }
30736        self.write_space();
30737        self.write_keyword(&e.kind);
30738        if let Some(expression) = &e.expression {
30739            self.write_space();
30740            self.generate_expression(expression)?;
30741        }
30742        Ok(())
30743    }
30744
30745    fn generate_view_attribute_property(&mut self, e: &ViewAttributeProperty) -> Result<()> {
30746        // Python: return self.sql(expression, "this")
30747        self.generate_expression(&e.this)?;
30748        Ok(())
30749    }
30750
30751    fn generate_volatile_property(&mut self, e: &VolatileProperty) -> Result<()> {
30752        // Python: return "VOLATILE" if expression.args.get("this") is None else "NOT VOLATILE"
30753        if e.this.is_some() {
30754            self.write_keyword("NOT VOLATILE");
30755        } else {
30756            self.write_keyword("VOLATILE");
30757        }
30758        Ok(())
30759    }
30760
30761    fn generate_watermark_column_constraint(&mut self, e: &WatermarkColumnConstraint) -> Result<()> {
30762        // Python: f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
30763        self.write_keyword("WATERMARK FOR");
30764        self.write_space();
30765        self.generate_expression(&e.this)?;
30766        self.write_space();
30767        self.write_keyword("AS");
30768        self.write_space();
30769        self.generate_expression(&e.expression)?;
30770        Ok(())
30771    }
30772
30773    fn generate_week(&mut self, e: &Week) -> Result<()> {
30774        // Python: return self.func("WEEK", expression.this, expression.args.get("mode"))
30775        self.write_keyword("WEEK");
30776        self.write("(");
30777        self.generate_expression(&e.this)?;
30778        if let Some(mode) = &e.mode {
30779            self.write(", ");
30780            self.generate_expression(mode)?;
30781        }
30782        self.write(")");
30783        Ok(())
30784    }
30785
30786    fn generate_when(&mut self, e: &When) -> Result<()> {
30787        // Python: WHEN {matched}{source}{condition} THEN {then}
30788        // matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
30789        // source = " BY SOURCE" if MATCHED_BY_SOURCE and expression.args.get("source") else ""
30790        self.write_keyword("WHEN");
30791        self.write_space();
30792
30793        // Check if matched
30794        if let Some(matched) = &e.matched {
30795            // Check the expression - if it's a boolean true, use MATCHED, otherwise NOT MATCHED
30796            match matched.as_ref() {
30797                Expression::Boolean(b) if b.value => {
30798                    self.write_keyword("MATCHED");
30799                }
30800                _ => {
30801                    self.write_keyword("NOT MATCHED");
30802                }
30803            }
30804        } else {
30805            self.write_keyword("NOT MATCHED");
30806        }
30807
30808        // BY SOURCE / BY TARGET
30809        // source = Boolean(true) means BY SOURCE, Boolean(false) means BY TARGET
30810        // BY TARGET is the default and typically omitted in output
30811        // Only emit if the dialect supports BY SOURCE syntax
30812        if self.config.matched_by_source {
30813            if let Some(source) = &e.source {
30814                if let Expression::Boolean(b) = source.as_ref() {
30815                    if b.value {
30816                        // BY SOURCE
30817                        self.write_space();
30818                        self.write_keyword("BY SOURCE");
30819                    }
30820                    // BY TARGET (b.value == false) is omitted as it's the default
30821                } else {
30822                    // For non-boolean source, output as BY SOURCE (legacy behavior)
30823                    self.write_space();
30824                    self.write_keyword("BY SOURCE");
30825                }
30826            }
30827        }
30828
30829        // Condition
30830        if let Some(condition) = &e.condition {
30831            self.write_space();
30832            self.write_keyword("AND");
30833            self.write_space();
30834            self.generate_expression(condition)?;
30835        }
30836
30837        self.write_space();
30838        self.write_keyword("THEN");
30839        self.write_space();
30840
30841        // Generate the then expression (could be INSERT, UPDATE, DELETE)
30842        // MERGE actions are stored as Tuples with the action keyword as first element
30843        self.generate_merge_action(&e.then)?;
30844
30845        Ok(())
30846    }
30847
30848    fn generate_merge_action(&mut self, action: &Expression) -> Result<()> {
30849        match action {
30850            Expression::Tuple(tuple) => {
30851                let elements = &tuple.expressions;
30852                if elements.is_empty() {
30853                    return self.generate_expression(action);
30854                }
30855                // Check if first element is a Var (INSERT, UPDATE, DELETE, etc.)
30856                match &elements[0] {
30857                    Expression::Var(v) if v.this == "INSERT" => {
30858                        self.write_keyword("INSERT");
30859                        let mut values_idx = 1;
30860                        // Check if second element is column list (Tuple)
30861                        if elements.len() > 1 {
30862                            if let Expression::Tuple(cols) = &elements[1] {
30863                                // Could be columns or values - if there's a third element, second is columns
30864                                if elements.len() > 2 {
30865                                    // Second is columns, third is values
30866                                    self.write(" (");
30867                                    for (i, col) in cols.expressions.iter().enumerate() {
30868                                        if i > 0 { self.write(", "); }
30869                                        self.generate_expression(col)?;
30870                                    }
30871                                    self.write(")");
30872                                    values_idx = 2;
30873                                } else {
30874                                    // Only two elements: INSERT + values (no explicit columns)
30875                                    values_idx = 1;
30876                                }
30877                            }
30878                        }
30879                        // Generate VALUES clause
30880                        if values_idx < elements.len() {
30881                            // Check if it's INSERT ROW (BigQuery) — no VALUES keyword needed
30882                            let is_row = matches!(&elements[values_idx], Expression::Var(v) if v.this == "ROW");
30883                            if !is_row {
30884                                self.write_space();
30885                                self.write_keyword("VALUES");
30886                            }
30887                            self.write(" ");
30888                            if let Expression::Tuple(vals) = &elements[values_idx] {
30889                                self.write("(");
30890                                for (i, val) in vals.expressions.iter().enumerate() {
30891                                    if i > 0 { self.write(", "); }
30892                                    self.generate_expression(val)?;
30893                                }
30894                                self.write(")");
30895                            } else {
30896                                self.generate_expression(&elements[values_idx])?;
30897                            }
30898                        }
30899                    }
30900                    Expression::Var(v) if v.this == "UPDATE" => {
30901                        self.write_keyword("UPDATE");
30902                        if elements.len() > 1 {
30903                            self.write_space();
30904                            self.write_keyword("SET");
30905                            // In pretty mode, put assignments on next line with extra indent
30906                            if self.config.pretty {
30907                                self.write_newline();
30908                                self.indent_level += 1;
30909                                self.write_indent();
30910                            } else {
30911                                self.write_space();
30912                            }
30913                            if let Expression::Tuple(assignments) = &elements[1] {
30914                                for (i, assignment) in assignments.expressions.iter().enumerate() {
30915                                    if i > 0 { self.write(", "); }
30916                                    // Strip MERGE target qualifiers from left side of UPDATE SET
30917                                    if !self.merge_strip_qualifiers.is_empty() {
30918                                        self.generate_merge_set_assignment(assignment)?;
30919                                    } else {
30920                                        self.generate_expression(assignment)?;
30921                                    }
30922                                }
30923                            } else {
30924                                self.generate_expression(&elements[1])?;
30925                            }
30926                            if self.config.pretty {
30927                                self.indent_level -= 1;
30928                            }
30929                        }
30930                    }
30931                    _ => {
30932                        // Fallback: generic tuple generation
30933                        self.generate_expression(action)?;
30934                    }
30935                }
30936            }
30937            Expression::Var(v) if v.this == "INSERT" || v.this == "UPDATE" || v.this == "DELETE" || v.this == "DO NOTHING" => {
30938                self.write_keyword(&v.this);
30939            }
30940            _ => {
30941                self.generate_expression(action)?;
30942            }
30943        }
30944        Ok(())
30945    }
30946
30947    /// Generate a MERGE UPDATE SET assignment, stripping target table qualifier from left side
30948    fn generate_merge_set_assignment(&mut self, assignment: &Expression) -> Result<()> {
30949        match assignment {
30950            Expression::Eq(eq) => {
30951                // Strip qualifier from the left side if it matches a MERGE target name
30952                let stripped_left = self.strip_merge_qualifier(&eq.left);
30953                self.generate_expression(&stripped_left)?;
30954                self.write(" = ");
30955                self.generate_expression(&eq.right)?;
30956                Ok(())
30957            }
30958            other => self.generate_expression(other),
30959        }
30960    }
30961
30962    /// Strip table qualifier from a column reference if it matches a MERGE target name
30963    fn strip_merge_qualifier(&self, expr: &Expression) -> Expression {
30964        match expr {
30965            Expression::Column(col) => {
30966                if let Some(ref table_ident) = col.table {
30967                    if self.merge_strip_qualifiers.iter().any(|n| n.eq_ignore_ascii_case(&table_ident.name)) {
30968                        // Strip the table qualifier
30969                        let mut col = col.clone();
30970                        col.table = None;
30971                        return Expression::Column(col);
30972                    }
30973                }
30974                expr.clone()
30975            }
30976            Expression::Dot(dot) => {
30977                // table.column -> column (strip qualifier)
30978                if let Expression::Identifier(id) = &dot.this {
30979                    if self.merge_strip_qualifiers.iter().any(|n| n.eq_ignore_ascii_case(&id.name)) {
30980                        return Expression::Identifier(dot.field.clone());
30981                    }
30982                }
30983                expr.clone()
30984            }
30985            _ => expr.clone(),
30986        }
30987    }
30988
30989    fn generate_whens(&mut self, e: &Whens) -> Result<()> {
30990        // Python: return self.expressions(expression, sep=" ", indent=False)
30991        for (i, expr) in e.expressions.iter().enumerate() {
30992            if i > 0 {
30993                // In pretty mode, each WHEN clause on its own line
30994                if self.config.pretty {
30995                    self.write_newline();
30996                    self.write_indent();
30997                } else {
30998                    self.write_space();
30999                }
31000            }
31001            self.generate_expression(expr)?;
31002        }
31003        Ok(())
31004    }
31005
31006    fn generate_where(&mut self, e: &Where) -> Result<()> {
31007        // Python: return f"{self.seg('WHERE')}{self.sep()}{this}"
31008        self.write_keyword("WHERE");
31009        self.write_space();
31010        self.generate_expression(&e.this)?;
31011        Ok(())
31012    }
31013
31014    fn generate_width_bucket(&mut self, e: &WidthBucket) -> Result<()> {
31015        // Python: return self.func("WIDTH_BUCKET", expression.this, ...)
31016        self.write_keyword("WIDTH_BUCKET");
31017        self.write("(");
31018        self.generate_expression(&e.this)?;
31019        if let Some(min_value) = &e.min_value {
31020            self.write(", ");
31021            self.generate_expression(min_value)?;
31022        }
31023        if let Some(max_value) = &e.max_value {
31024            self.write(", ");
31025            self.generate_expression(max_value)?;
31026        }
31027        if let Some(num_buckets) = &e.num_buckets {
31028            self.write(", ");
31029            self.generate_expression(num_buckets)?;
31030        }
31031        self.write(")");
31032        Ok(())
31033    }
31034
31035    fn generate_window(&mut self, e: &WindowSpec) -> Result<()> {
31036        // Window specification: PARTITION BY ... ORDER BY ... frame
31037        self.generate_window_spec(e)
31038    }
31039
31040    fn generate_window_spec(&mut self, e: &WindowSpec) -> Result<()> {
31041        // Window specification: PARTITION BY ... ORDER BY ... frame
31042        let mut has_content = false;
31043
31044        // PARTITION BY
31045        if !e.partition_by.is_empty() {
31046            self.write_keyword("PARTITION BY");
31047            self.write_space();
31048            for (i, expr) in e.partition_by.iter().enumerate() {
31049                if i > 0 {
31050                    self.write(", ");
31051                }
31052                self.generate_expression(expr)?;
31053            }
31054            has_content = true;
31055        }
31056
31057        // ORDER BY
31058        if !e.order_by.is_empty() {
31059            if has_content {
31060                self.write_space();
31061            }
31062            self.write_keyword("ORDER BY");
31063            self.write_space();
31064            for (i, ordered) in e.order_by.iter().enumerate() {
31065                if i > 0 {
31066                    self.write(", ");
31067                }
31068                self.generate_expression(&ordered.this)?;
31069                if ordered.desc {
31070                    self.write_space();
31071                    self.write_keyword("DESC");
31072                } else if ordered.explicit_asc {
31073                    self.write_space();
31074                    self.write_keyword("ASC");
31075                }
31076                if let Some(nulls_first) = ordered.nulls_first {
31077                    self.write_space();
31078                    self.write_keyword("NULLS");
31079                    self.write_space();
31080                    if nulls_first {
31081                        self.write_keyword("FIRST");
31082                    } else {
31083                        self.write_keyword("LAST");
31084                    }
31085                }
31086            }
31087            has_content = true;
31088        }
31089
31090        // Frame specification
31091        if let Some(frame) = &e.frame {
31092            if has_content {
31093                self.write_space();
31094            }
31095            self.generate_window_frame(frame)?;
31096        }
31097
31098        Ok(())
31099    }
31100
31101    fn generate_with_data_property(&mut self, e: &WithDataProperty) -> Result<()> {
31102        // Python: f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
31103        self.write_keyword("WITH");
31104        self.write_space();
31105        if e.no.is_some() {
31106            self.write_keyword("NO");
31107            self.write_space();
31108        }
31109        self.write_keyword("DATA");
31110
31111        // statistics
31112        if let Some(statistics) = &e.statistics {
31113            self.write_space();
31114            self.write_keyword("AND");
31115            self.write_space();
31116            // Check if statistics is true or false
31117            match statistics.as_ref() {
31118                Expression::Boolean(b) if !b.value => {
31119                    self.write_keyword("NO");
31120                    self.write_space();
31121                }
31122                _ => {}
31123            }
31124            self.write_keyword("STATISTICS");
31125        }
31126        Ok(())
31127    }
31128
31129    fn generate_with_fill(&mut self, e: &WithFill) -> Result<()> {
31130        // Python: f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
31131        self.write_keyword("WITH FILL");
31132
31133        if let Some(from_) = &e.from_ {
31134            self.write_space();
31135            self.write_keyword("FROM");
31136            self.write_space();
31137            self.generate_expression(from_)?;
31138        }
31139
31140        if let Some(to) = &e.to {
31141            self.write_space();
31142            self.write_keyword("TO");
31143            self.write_space();
31144            self.generate_expression(to)?;
31145        }
31146
31147        if let Some(step) = &e.step {
31148            self.write_space();
31149            self.write_keyword("STEP");
31150            self.write_space();
31151            self.generate_expression(step)?;
31152        }
31153
31154        if let Some(interpolate) = &e.interpolate {
31155            self.write_space();
31156            self.write_keyword("INTERPOLATE");
31157            self.write(" (");
31158            // INTERPOLATE items use reversed alias format: name AS expression
31159            self.generate_interpolate_item(interpolate)?;
31160            self.write(")");
31161        }
31162
31163        Ok(())
31164    }
31165
31166    /// Generate INTERPOLATE items with reversed alias format (name AS expression)
31167    fn generate_interpolate_item(&mut self, expr: &Expression) -> Result<()> {
31168        match expr {
31169            Expression::Alias(alias) => {
31170                // Output as: alias_name AS expression
31171                self.generate_identifier(&alias.alias)?;
31172                self.write_space();
31173                self.write_keyword("AS");
31174                self.write_space();
31175                self.generate_expression(&alias.this)?;
31176            }
31177            Expression::Tuple(tuple) => {
31178                for (i, item) in tuple.expressions.iter().enumerate() {
31179                    if i > 0 { self.write(", "); }
31180                    self.generate_interpolate_item(item)?;
31181                }
31182            }
31183            other => {
31184                self.generate_expression(other)?;
31185            }
31186        }
31187        Ok(())
31188    }
31189
31190    fn generate_with_journal_table_property(&mut self, e: &WithJournalTableProperty) -> Result<()> {
31191        // Python: return f"WITH JOURNAL TABLE={self.sql(expression, 'this')}"
31192        self.write_keyword("WITH JOURNAL TABLE");
31193        self.write("=");
31194        self.generate_expression(&e.this)?;
31195        Ok(())
31196    }
31197
31198    fn generate_with_operator(&mut self, e: &WithOperator) -> Result<()> {
31199        // Python: return f"{self.sql(expression, 'this')} WITH {self.sql(expression, 'op')}"
31200        self.generate_expression(&e.this)?;
31201        self.write_space();
31202        self.write_keyword("WITH");
31203        self.write_space();
31204        self.write_keyword(&e.op);
31205        Ok(())
31206    }
31207
31208    fn generate_with_procedure_options(&mut self, e: &WithProcedureOptions) -> Result<()> {
31209        // Python: return f"WITH {self.expressions(expression, flat=True)}"
31210        self.write_keyword("WITH");
31211        self.write_space();
31212        for (i, expr) in e.expressions.iter().enumerate() {
31213            if i > 0 {
31214                self.write(", ");
31215            }
31216            self.generate_expression(expr)?;
31217        }
31218        Ok(())
31219    }
31220
31221    fn generate_with_schema_binding_property(&mut self, e: &WithSchemaBindingProperty) -> Result<()> {
31222        // Python: return f"WITH {self.sql(expression, 'this')}"
31223        self.write_keyword("WITH");
31224        self.write_space();
31225        self.generate_expression(&e.this)?;
31226        Ok(())
31227    }
31228
31229    fn generate_with_system_versioning_property(&mut self, e: &WithSystemVersioningProperty) -> Result<()> {
31230        // Python: complex logic for SYSTEM_VERSIONING with options
31231        // SYSTEM_VERSIONING=ON(HISTORY_TABLE=..., DATA_CONSISTENCY_CHECK=..., HISTORY_RETENTION_PERIOD=...)
31232        // or SYSTEM_VERSIONING=ON/OFF
31233        // with WITH(...) wrapper if with_ is set
31234
31235        let mut parts = Vec::new();
31236
31237        if let Some(this) = &e.this {
31238            // HISTORY_TABLE=...
31239            let mut s = String::from("HISTORY_TABLE=");
31240            let mut gen = Generator::new();
31241            gen.generate_expression(this)?;
31242            s.push_str(&gen.output);
31243            parts.push(s);
31244        }
31245
31246        if let Some(data_consistency) = &e.data_consistency {
31247            let mut s = String::from("DATA_CONSISTENCY_CHECK=");
31248            let mut gen = Generator::new();
31249            gen.generate_expression(data_consistency)?;
31250            s.push_str(&gen.output);
31251            parts.push(s);
31252        }
31253
31254        if let Some(retention_period) = &e.retention_period {
31255            let mut s = String::from("HISTORY_RETENTION_PERIOD=");
31256            let mut gen = Generator::new();
31257            gen.generate_expression(retention_period)?;
31258            s.push_str(&gen.output);
31259            parts.push(s);
31260        }
31261
31262        self.write_keyword("SYSTEM_VERSIONING");
31263        self.write("=");
31264
31265        if !parts.is_empty() {
31266            self.write_keyword("ON");
31267            self.write("(");
31268            self.write(&parts.join(", "));
31269            self.write(")");
31270        } else if e.on.is_some() {
31271            self.write_keyword("ON");
31272        } else {
31273            self.write_keyword("OFF");
31274        }
31275
31276        // Wrap in WITH(...) if with_ is set
31277        if e.with_.is_some() {
31278            let inner = self.output.clone();
31279            self.output.clear();
31280            self.write("WITH(");
31281            self.write(&inner);
31282            self.write(")");
31283        }
31284
31285        Ok(())
31286    }
31287
31288    fn generate_with_table_hint(&mut self, e: &WithTableHint) -> Result<()> {
31289        // Python: f"WITH ({self.expressions(expression, flat=True)})"
31290        self.write_keyword("WITH");
31291        self.write(" (");
31292        for (i, expr) in e.expressions.iter().enumerate() {
31293            if i > 0 {
31294                self.write(", ");
31295            }
31296            self.generate_expression(expr)?;
31297        }
31298        self.write(")");
31299        Ok(())
31300    }
31301
31302    fn generate_xml_element(&mut self, e: &XMLElement) -> Result<()> {
31303        // Python: prefix = "EVALNAME" if expression.args.get("evalname") else "NAME"
31304        // return self.func("XMLELEMENT", name, *expression.expressions)
31305        self.write_keyword("XMLELEMENT");
31306        self.write("(");
31307
31308        if e.evalname.is_some() {
31309            self.write_keyword("EVALNAME");
31310        } else {
31311            self.write_keyword("NAME");
31312        }
31313        self.write_space();
31314        self.generate_expression(&e.this)?;
31315
31316        for expr in &e.expressions {
31317            self.write(", ");
31318            self.generate_expression(expr)?;
31319        }
31320        self.write(")");
31321        Ok(())
31322    }
31323
31324    fn generate_xml_get(&mut self, e: &XMLGet) -> Result<()> {
31325        // XMLGET(this, expression [, instance])
31326        self.write_keyword("XMLGET");
31327        self.write("(");
31328        self.generate_expression(&e.this)?;
31329        self.write(", ");
31330        self.generate_expression(&e.expression)?;
31331        if let Some(instance) = &e.instance {
31332            self.write(", ");
31333            self.generate_expression(instance)?;
31334        }
31335        self.write(")");
31336        Ok(())
31337    }
31338
31339    fn generate_xml_key_value_option(&mut self, e: &XMLKeyValueOption) -> Result<()> {
31340        // Python: this + optional (expr)
31341        self.generate_expression(&e.this)?;
31342        if let Some(expression) = &e.expression {
31343            self.write("(");
31344            self.generate_expression(expression)?;
31345            self.write(")");
31346        }
31347        Ok(())
31348    }
31349
31350    fn generate_xml_table(&mut self, e: &XMLTable) -> Result<()> {
31351        // Python: XMLTABLE(namespaces + this + passing + by_ref + columns)
31352        self.write_keyword("XMLTABLE");
31353        self.write("(");
31354
31355        if self.config.pretty {
31356            self.indent_level += 1;
31357            self.write_newline();
31358            self.write_indent();
31359            self.generate_expression(&e.this)?;
31360
31361            if let Some(passing) = &e.passing {
31362                self.write_newline();
31363                self.write_indent();
31364                self.write_keyword("PASSING");
31365                if let Expression::Tuple(tuple) = passing.as_ref() {
31366                    for expr in &tuple.expressions {
31367                        self.write_newline();
31368                        self.indent_level += 1;
31369                        self.write_indent();
31370                        self.generate_expression(expr)?;
31371                        self.indent_level -= 1;
31372                    }
31373                } else {
31374                    self.write_newline();
31375                    self.indent_level += 1;
31376                    self.write_indent();
31377                    self.generate_expression(passing)?;
31378                    self.indent_level -= 1;
31379                }
31380            }
31381
31382            if e.by_ref.is_some() {
31383                self.write_newline();
31384                self.write_indent();
31385                self.write_keyword("RETURNING SEQUENCE BY REF");
31386            }
31387
31388            if !e.columns.is_empty() {
31389                self.write_newline();
31390                self.write_indent();
31391                self.write_keyword("COLUMNS");
31392                for (i, col) in e.columns.iter().enumerate() {
31393                    self.write_newline();
31394                    self.indent_level += 1;
31395                    self.write_indent();
31396                    self.generate_expression(col)?;
31397                    self.indent_level -= 1;
31398                    if i < e.columns.len() - 1 {
31399                        self.write(",");
31400                    }
31401                }
31402            }
31403
31404            self.indent_level -= 1;
31405            self.write_newline();
31406            self.write_indent();
31407            self.write(")");
31408            return Ok(());
31409        }
31410
31411        // Namespaces - unwrap Tuple to generate comma-separated list without parentheses
31412        if let Some(namespaces) = &e.namespaces {
31413            self.write_keyword("XMLNAMESPACES");
31414            self.write("(");
31415            // Unwrap Tuple if present to avoid extra parentheses
31416            if let Expression::Tuple(tuple) = namespaces.as_ref() {
31417                for (i, expr) in tuple.expressions.iter().enumerate() {
31418                    if i > 0 {
31419                        self.write(", ");
31420                    }
31421                    // Python pattern: if it's an Alias, output as-is; otherwise prepend DEFAULT
31422                    // See xmlnamespace_sql in generator.py
31423                    if !matches!(expr, Expression::Alias(_)) {
31424                        self.write_keyword("DEFAULT");
31425                        self.write_space();
31426                    }
31427                    self.generate_expression(expr)?;
31428                }
31429            } else {
31430                // Single namespace - check if DEFAULT
31431                if !matches!(namespaces.as_ref(), Expression::Alias(_)) {
31432                    self.write_keyword("DEFAULT");
31433                    self.write_space();
31434                }
31435                self.generate_expression(namespaces)?;
31436            }
31437            self.write("), ");
31438        }
31439
31440        // XPath expression
31441        self.generate_expression(&e.this)?;
31442
31443        // PASSING clause - unwrap Tuple to generate comma-separated list without parentheses
31444        if let Some(passing) = &e.passing {
31445            self.write_space();
31446            self.write_keyword("PASSING");
31447            self.write_space();
31448            // Unwrap Tuple if present to avoid extra parentheses
31449            if let Expression::Tuple(tuple) = passing.as_ref() {
31450                for (i, expr) in tuple.expressions.iter().enumerate() {
31451                    if i > 0 {
31452                        self.write(", ");
31453                    }
31454                    self.generate_expression(expr)?;
31455                }
31456            } else {
31457                self.generate_expression(passing)?;
31458            }
31459        }
31460
31461        // RETURNING SEQUENCE BY REF
31462        if e.by_ref.is_some() {
31463            self.write_space();
31464            self.write_keyword("RETURNING SEQUENCE BY REF");
31465        }
31466
31467        // COLUMNS clause
31468        if !e.columns.is_empty() {
31469            self.write_space();
31470            self.write_keyword("COLUMNS");
31471            self.write_space();
31472            for (i, col) in e.columns.iter().enumerate() {
31473                if i > 0 {
31474                    self.write(", ");
31475                }
31476                self.generate_expression(col)?;
31477            }
31478        }
31479
31480        self.write(")");
31481        Ok(())
31482    }
31483
31484    fn generate_xor(&mut self, e: &Xor) -> Result<()> {
31485        // Python: return self.connector_sql(expression, "XOR", stack)
31486        // Handles: this XOR expression or expressions joined by XOR
31487        if let Some(this) = &e.this {
31488            self.generate_expression(this)?;
31489            if let Some(expression) = &e.expression {
31490                self.write_space();
31491                self.write_keyword("XOR");
31492                self.write_space();
31493                self.generate_expression(expression)?;
31494            }
31495        }
31496
31497        // Handle multiple expressions
31498        for (i, expr) in e.expressions.iter().enumerate() {
31499            if i > 0 || e.this.is_some() {
31500                self.write_space();
31501                self.write_keyword("XOR");
31502                self.write_space();
31503            }
31504            self.generate_expression(expr)?;
31505        }
31506        Ok(())
31507    }
31508
31509    fn generate_zipf(&mut self, e: &Zipf) -> Result<()> {
31510        // ZIPF(this, elementcount [, gen])
31511        self.write_keyword("ZIPF");
31512        self.write("(");
31513        self.generate_expression(&e.this)?;
31514        if let Some(elementcount) = &e.elementcount {
31515            self.write(", ");
31516            self.generate_expression(elementcount)?;
31517        }
31518        if let Some(gen) = &e.gen {
31519            self.write(", ");
31520            self.generate_expression(gen)?;
31521        }
31522        self.write(")");
31523        Ok(())
31524    }
31525}
31526
31527impl Default for Generator {
31528    fn default() -> Self {
31529        Self::new()
31530    }
31531}
31532
31533#[cfg(test)]
31534mod tests {
31535    use super::*;
31536    use crate::parser::Parser;
31537
31538    fn roundtrip(sql: &str) -> String {
31539        let ast = Parser::parse_sql(sql).unwrap();
31540        Generator::sql(&ast[0]).unwrap()
31541    }
31542
31543    #[test]
31544    fn test_simple_select() {
31545        let result = roundtrip("SELECT 1");
31546        assert_eq!(result, "SELECT 1");
31547    }
31548
31549    #[test]
31550    fn test_select_from() {
31551        let result = roundtrip("SELECT a, b FROM t");
31552        assert_eq!(result, "SELECT a, b FROM t");
31553    }
31554
31555    #[test]
31556    fn test_select_where() {
31557        let result = roundtrip("SELECT * FROM t WHERE x = 1");
31558        assert_eq!(result, "SELECT * FROM t WHERE x = 1");
31559    }
31560
31561    #[test]
31562    fn test_select_join() {
31563        let result = roundtrip("SELECT * FROM a JOIN b ON a.id = b.id");
31564        assert_eq!(result, "SELECT * FROM a JOIN b ON a.id = b.id");
31565    }
31566
31567    #[test]
31568    fn test_insert() {
31569        let result = roundtrip("INSERT INTO t (a, b) VALUES (1, 2)");
31570        assert_eq!(result, "INSERT INTO t (a, b) VALUES (1, 2)");
31571    }
31572
31573    #[test]
31574    fn test_pretty_print() {
31575        let ast = Parser::parse_sql("SELECT a, b FROM t WHERE x = 1").unwrap();
31576        let result = Generator::pretty_sql(&ast[0]).unwrap();
31577        assert!(result.contains('\n'));
31578    }
31579
31580    #[test]
31581    fn test_window_function() {
31582        let result = roundtrip("SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)");
31583        assert_eq!(result, "SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)");
31584    }
31585
31586    #[test]
31587    fn test_window_function_with_frame() {
31588        let result = roundtrip("SELECT SUM(amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)");
31589        assert_eq!(result, "SELECT SUM(amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)");
31590    }
31591
31592    #[test]
31593    fn test_aggregate_with_filter() {
31594        let result = roundtrip("SELECT COUNT(*) FILTER (WHERE status = 1) FROM orders");
31595        assert_eq!(result, "SELECT COUNT(*) FILTER(WHERE status = 1) FROM orders");
31596    }
31597
31598    #[test]
31599    fn test_subscript() {
31600        let result = roundtrip("SELECT arr[0]");
31601        assert_eq!(result, "SELECT arr[0]");
31602    }
31603
31604    // DDL tests
31605    #[test]
31606    fn test_create_table() {
31607        let result = roundtrip("CREATE TABLE users (id INT, name VARCHAR(100))");
31608        assert_eq!(result, "CREATE TABLE users (id INT, name VARCHAR(100))");
31609    }
31610
31611    #[test]
31612    fn test_create_table_with_constraints() {
31613        let result = roundtrip("CREATE TABLE users (id INT PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL)");
31614        assert_eq!(result, "CREATE TABLE users (id INT PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL)");
31615    }
31616
31617    #[test]
31618    fn test_create_table_if_not_exists() {
31619        let result = roundtrip("CREATE TABLE IF NOT EXISTS t (id INT)");
31620        assert_eq!(result, "CREATE TABLE IF NOT EXISTS t (id INT)");
31621    }
31622
31623    #[test]
31624    fn test_drop_table() {
31625        let result = roundtrip("DROP TABLE users");
31626        assert_eq!(result, "DROP TABLE users");
31627    }
31628
31629    #[test]
31630    fn test_drop_table_if_exists_cascade() {
31631        let result = roundtrip("DROP TABLE IF EXISTS users CASCADE");
31632        assert_eq!(result, "DROP TABLE IF EXISTS users CASCADE");
31633    }
31634
31635    #[test]
31636    fn test_alter_table_add_column() {
31637        let result = roundtrip("ALTER TABLE users ADD COLUMN email VARCHAR(255)");
31638        assert_eq!(result, "ALTER TABLE users ADD COLUMN email VARCHAR(255)");
31639    }
31640
31641    #[test]
31642    fn test_alter_table_drop_column() {
31643        let result = roundtrip("ALTER TABLE users DROP COLUMN email");
31644        assert_eq!(result, "ALTER TABLE users DROP COLUMN email");
31645    }
31646
31647    #[test]
31648    fn test_create_index() {
31649        let result = roundtrip("CREATE INDEX idx_name ON users(name)");
31650        assert_eq!(result, "CREATE INDEX idx_name ON users(name)");
31651    }
31652
31653    #[test]
31654    fn test_create_unique_index() {
31655        let result = roundtrip("CREATE UNIQUE INDEX idx_email ON users(email)");
31656        assert_eq!(result, "CREATE UNIQUE INDEX idx_email ON users(email)");
31657    }
31658
31659    #[test]
31660    fn test_drop_index() {
31661        let result = roundtrip("DROP INDEX idx_name");
31662        assert_eq!(result, "DROP INDEX idx_name");
31663    }
31664
31665    #[test]
31666    fn test_create_view() {
31667        let result = roundtrip("CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1");
31668        assert_eq!(result, "CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1");
31669    }
31670
31671    #[test]
31672    fn test_drop_view() {
31673        let result = roundtrip("DROP VIEW active_users");
31674        assert_eq!(result, "DROP VIEW active_users");
31675    }
31676
31677    #[test]
31678    fn test_truncate() {
31679        let result = roundtrip("TRUNCATE TABLE users");
31680        assert_eq!(result, "TRUNCATE TABLE users");
31681    }
31682
31683    #[test]
31684    fn test_string_literal_escaping_default() {
31685        // Default: double single quotes
31686        let result = roundtrip("SELECT 'hello'");
31687        assert_eq!(result, "SELECT 'hello'");
31688
31689        // Single quotes are doubled
31690        let result = roundtrip("SELECT 'it''s a test'");
31691        assert_eq!(result, "SELECT 'it''s a test'");
31692    }
31693
31694    #[test]
31695    fn test_string_literal_escaping_mysql() {
31696        use crate::dialects::DialectType;
31697
31698        let config = GeneratorConfig {
31699            dialect: Some(DialectType::MySQL),
31700            ..Default::default()
31701        };
31702
31703        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31704        let mut gen = Generator::with_config(config.clone());
31705        let result = gen.generate(&ast[0]).unwrap();
31706        assert_eq!(result, "SELECT 'hello'");
31707
31708        // MySQL uses SQL standard quote doubling for escaping (matches Python sqlglot)
31709        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31710        let mut gen = Generator::with_config(config.clone());
31711        let result = gen.generate(&ast[0]).unwrap();
31712        assert_eq!(result, "SELECT 'it''s'");
31713    }
31714
31715    #[test]
31716    fn test_string_literal_escaping_postgres() {
31717        use crate::dialects::DialectType;
31718
31719        let config = GeneratorConfig {
31720            dialect: Some(DialectType::PostgreSQL),
31721            ..Default::default()
31722        };
31723
31724        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31725        let mut gen = Generator::with_config(config.clone());
31726        let result = gen.generate(&ast[0]).unwrap();
31727        assert_eq!(result, "SELECT 'hello'");
31728
31729        // PostgreSQL uses doubled quotes for regular strings
31730        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31731        let mut gen = Generator::with_config(config.clone());
31732        let result = gen.generate(&ast[0]).unwrap();
31733        assert_eq!(result, "SELECT 'it''s'");
31734    }
31735
31736    #[test]
31737    fn test_string_literal_escaping_bigquery() {
31738        use crate::dialects::DialectType;
31739
31740        let config = GeneratorConfig {
31741            dialect: Some(DialectType::BigQuery),
31742            ..Default::default()
31743        };
31744
31745        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31746        let mut gen = Generator::with_config(config.clone());
31747        let result = gen.generate(&ast[0]).unwrap();
31748        assert_eq!(result, "SELECT 'hello'");
31749
31750        // BigQuery escapes single quotes with backslash
31751        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31752        let mut gen = Generator::with_config(config.clone());
31753        let result = gen.generate(&ast[0]).unwrap();
31754        assert_eq!(result, "SELECT 'it\\'s'");
31755    }
31756
31757}