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::Ordered(ordered) => self.generate_ordered(ordered),
2010            Expression::DataType(dt) => self.generate_data_type(dt),
2011            Expression::Raw(raw) => {
2012                self.write(&raw.sql);
2013                Ok(())
2014            }
2015            Expression::Command(cmd) => {
2016                self.write(&cmd.this);
2017                Ok(())
2018            }
2019            Expression::Kill(kill) => {
2020                self.write_keyword("KILL");
2021                if let Some(kind) = &kill.kind {
2022                    self.write_space();
2023                    self.write_keyword(kind);
2024                }
2025                self.write_space();
2026                self.generate_expression(&kill.this)?;
2027                Ok(())
2028            }
2029            Expression::Execute(exec) => {
2030                self.write_keyword("EXEC");
2031                self.write_space();
2032                self.generate_expression(&exec.this)?;
2033                for (i, param) in exec.parameters.iter().enumerate() {
2034                    if i == 0 {
2035                        self.write_space();
2036                    } else {
2037                        self.write(", ");
2038                    }
2039                    self.write(&param.name);
2040                    self.write("=");
2041                    self.generate_expression(&param.value)?;
2042                }
2043                Ok(())
2044            }
2045            Expression::Annotated(annotated) => {
2046                self.generate_expression(&annotated.this)?;
2047                for comment in &annotated.trailing_comments {
2048                    self.write(" ");
2049                    self.write_formatted_comment(comment);
2050                }
2051                Ok(())
2052            }
2053
2054            // DDL statements
2055            Expression::CreateTable(ct) => self.generate_create_table(ct),
2056            Expression::DropTable(dt) => self.generate_drop_table(dt),
2057            Expression::AlterTable(at) => self.generate_alter_table(at),
2058            Expression::CreateIndex(ci) => self.generate_create_index(ci),
2059            Expression::DropIndex(di) => self.generate_drop_index(di),
2060            Expression::CreateView(cv) => self.generate_create_view(cv),
2061            Expression::DropView(dv) => self.generate_drop_view(dv),
2062            Expression::AlterView(av) => self.generate_alter_view(av),
2063            Expression::AlterIndex(ai) => self.generate_alter_index(ai),
2064            Expression::Truncate(tr) => self.generate_truncate(tr),
2065            Expression::Use(u) => self.generate_use(u),
2066            // Phase 4: Additional DDL statements
2067            Expression::CreateSchema(cs) => self.generate_create_schema(cs),
2068            Expression::DropSchema(ds) => self.generate_drop_schema(ds),
2069            Expression::DropNamespace(dn) => self.generate_drop_namespace(dn),
2070            Expression::CreateDatabase(cd) => self.generate_create_database(cd),
2071            Expression::DropDatabase(dd) => self.generate_drop_database(dd),
2072            Expression::CreateFunction(cf) => self.generate_create_function(cf),
2073            Expression::DropFunction(df) => self.generate_drop_function(df),
2074            Expression::CreateProcedure(cp) => self.generate_create_procedure(cp),
2075            Expression::DropProcedure(dp) => self.generate_drop_procedure(dp),
2076            Expression::CreateSequence(cs) => self.generate_create_sequence(cs),
2077            Expression::DropSequence(ds) => self.generate_drop_sequence(ds),
2078            Expression::AlterSequence(als) => self.generate_alter_sequence(als),
2079            Expression::CreateTrigger(ct) => self.generate_create_trigger(ct),
2080            Expression::DropTrigger(dt) => self.generate_drop_trigger(dt),
2081            Expression::CreateType(ct) => self.generate_create_type(ct),
2082            Expression::DropType(dt) => self.generate_drop_type(dt),
2083            Expression::Describe(d) => self.generate_describe(d),
2084            Expression::Show(s) => self.generate_show(s),
2085
2086            // CACHE/UNCACHE/LOAD TABLE (Spark/Hive)
2087            Expression::Cache(c) => self.generate_cache(c),
2088            Expression::Uncache(u) => self.generate_uncache(u),
2089            Expression::LoadData(l) => self.generate_load_data(l),
2090            Expression::Pragma(p) => self.generate_pragma(p),
2091            Expression::Grant(g) => self.generate_grant(g),
2092            Expression::Revoke(r) => self.generate_revoke(r),
2093            Expression::Comment(c) => self.generate_comment(c),
2094            Expression::SetStatement(s) => self.generate_set_statement(s),
2095
2096            // PIVOT/UNPIVOT
2097            Expression::Pivot(pivot) => self.generate_pivot(pivot),
2098            Expression::Unpivot(unpivot) => self.generate_unpivot(unpivot),
2099
2100            // VALUES table constructor
2101            Expression::Values(values) => self.generate_values(values),
2102
2103
2104            // === BATCH-GENERATED MATCH ARMS (481 variants) ===
2105            Expression::AIAgg(e) => self.generate_ai_agg(e),
2106            Expression::AIClassify(e) => self.generate_ai_classify(e),
2107            Expression::AddPartition(e) => self.generate_add_partition(e),
2108            Expression::AlgorithmProperty(e) => self.generate_algorithm_property(e),
2109            Expression::Aliases(e) => self.generate_aliases(e),
2110            Expression::AllowedValuesProperty(e) => self.generate_allowed_values_property(e),
2111            Expression::AlterColumn(e) => self.generate_alter_column(e),
2112            Expression::AlterSession(e) => self.generate_alter_session(e),
2113            Expression::AlterSet(e) => self.generate_alter_set(e),
2114            Expression::AlterSortKey(e) => self.generate_alter_sort_key(e),
2115            Expression::Analyze(e) => self.generate_analyze(e),
2116            Expression::AnalyzeDelete(e) => self.generate_analyze_delete(e),
2117            Expression::AnalyzeHistogram(e) => self.generate_analyze_histogram(e),
2118            Expression::AnalyzeListChainedRows(e) => self.generate_analyze_list_chained_rows(e),
2119            Expression::AnalyzeSample(e) => self.generate_analyze_sample(e),
2120            Expression::AnalyzeStatistics(e) => self.generate_analyze_statistics(e),
2121            Expression::AnalyzeValidate(e) => self.generate_analyze_validate(e),
2122            Expression::AnalyzeWith(e) => self.generate_analyze_with(e),
2123            Expression::Anonymous(e) => self.generate_anonymous(e),
2124            Expression::AnonymousAggFunc(e) => self.generate_anonymous_agg_func(e),
2125            Expression::Apply(e) => self.generate_apply(e),
2126            Expression::ApproxPercentileEstimate(e) => self.generate_approx_percentile_estimate(e),
2127            Expression::ApproxQuantile(e) => self.generate_approx_quantile(e),
2128            Expression::ApproxQuantiles(e) => self.generate_approx_quantiles(e),
2129            Expression::ApproxTopK(e) => self.generate_approx_top_k(e),
2130            Expression::ApproxTopKAccumulate(e) => self.generate_approx_top_k_accumulate(e),
2131            Expression::ApproxTopKCombine(e) => self.generate_approx_top_k_combine(e),
2132            Expression::ApproxTopKEstimate(e) => self.generate_approx_top_k_estimate(e),
2133            Expression::ApproxTopSum(e) => self.generate_approx_top_sum(e),
2134            Expression::ArgMax(e) => self.generate_arg_max(e),
2135            Expression::ArgMin(e) => self.generate_arg_min(e),
2136            Expression::ArrayAll(e) => self.generate_array_all(e),
2137            Expression::ArrayAny(e) => self.generate_array_any(e),
2138            Expression::ArrayConstructCompact(e) => self.generate_array_construct_compact(e),
2139            Expression::ArraySum(e) => self.generate_array_sum(e),
2140            Expression::AtIndex(e) => self.generate_at_index(e),
2141            Expression::Attach(e) => self.generate_attach(e),
2142            Expression::AttachOption(e) => self.generate_attach_option(e),
2143            Expression::AutoIncrementProperty(e) => self.generate_auto_increment_property(e),
2144            Expression::AutoRefreshProperty(e) => self.generate_auto_refresh_property(e),
2145            Expression::BackupProperty(e) => self.generate_backup_property(e),
2146            Expression::Base64DecodeBinary(e) => self.generate_base64_decode_binary(e),
2147            Expression::Base64DecodeString(e) => self.generate_base64_decode_string(e),
2148            Expression::Base64Encode(e) => self.generate_base64_encode(e),
2149            Expression::BlockCompressionProperty(e) => self.generate_block_compression_property(e),
2150            Expression::Booland(e) => self.generate_booland(e),
2151            Expression::Boolor(e) => self.generate_boolor(e),
2152            Expression::BuildProperty(e) => self.generate_build_property(e),
2153            Expression::ByteString(e) => self.generate_byte_string(e),
2154            Expression::CaseSpecificColumnConstraint(e) => self.generate_case_specific_column_constraint(e),
2155            Expression::CastToStrType(e) => self.generate_cast_to_str_type(e),
2156            Expression::Changes(e) => self.generate_changes(e),
2157            Expression::CharacterSetColumnConstraint(e) => self.generate_character_set_column_constraint(e),
2158            Expression::CharacterSetProperty(e) => self.generate_character_set_property(e),
2159            Expression::CheckColumnConstraint(e) => self.generate_check_column_constraint(e),
2160            Expression::CheckJson(e) => self.generate_check_json(e),
2161            Expression::CheckXml(e) => self.generate_check_xml(e),
2162            Expression::ChecksumProperty(e) => self.generate_checksum_property(e),
2163            Expression::Clone(e) => self.generate_clone(e),
2164            Expression::ClusterBy(e) => self.generate_cluster_by(e),
2165            Expression::ClusteredByProperty(e) => self.generate_clustered_by_property(e),
2166            Expression::CollateProperty(e) => self.generate_collate_property(e),
2167            Expression::ColumnConstraint(e) => self.generate_column_constraint(e),
2168            Expression::ColumnDef(e) => self.generate_column_def_expr(e),
2169            Expression::ColumnPosition(e) => self.generate_column_position(e),
2170            Expression::ColumnPrefix(e) => self.generate_column_prefix(e),
2171            Expression::Columns(e) => self.generate_columns(e),
2172            Expression::CombinedAggFunc(e) => self.generate_combined_agg_func(e),
2173            Expression::CombinedParameterizedAgg(e) => self.generate_combined_parameterized_agg(e),
2174            Expression::Commit(e) => self.generate_commit(e),
2175            Expression::Comprehension(e) => self.generate_comprehension(e),
2176            Expression::Compress(e) => self.generate_compress(e),
2177            Expression::CompressColumnConstraint(e) => self.generate_compress_column_constraint(e),
2178            Expression::ComputedColumnConstraint(e) => self.generate_computed_column_constraint(e),
2179            Expression::ConditionalInsert(e) => self.generate_conditional_insert(e),
2180            Expression::Constraint(e) => self.generate_constraint(e),
2181            Expression::ConvertTimezone(e) => self.generate_convert_timezone(e),
2182            Expression::ConvertToCharset(e) => self.generate_convert_to_charset(e),
2183            Expression::Copy(e) => self.generate_copy(e),
2184            Expression::CopyParameter(e) => self.generate_copy_parameter(e),
2185            Expression::Corr(e) => self.generate_corr(e),
2186            Expression::CosineDistance(e) => self.generate_cosine_distance(e),
2187            Expression::CovarPop(e) => self.generate_covar_pop(e),
2188            Expression::CovarSamp(e) => self.generate_covar_samp(e),
2189            Expression::Credentials(e) => self.generate_credentials(e),
2190            Expression::CredentialsProperty(e) => self.generate_credentials_property(e),
2191            Expression::Cte(e) => self.generate_cte(e),
2192            Expression::Cube(e) => self.generate_cube(e),
2193            Expression::CurrentDatetime(e) => self.generate_current_datetime(e),
2194            Expression::CurrentSchema(e) => self.generate_current_schema(e),
2195            Expression::CurrentSchemas(e) => self.generate_current_schemas(e),
2196            Expression::CurrentUser(e) => self.generate_current_user(e),
2197            Expression::DPipe(e) => self.generate_d_pipe(e),
2198            Expression::DataBlocksizeProperty(e) => self.generate_data_blocksize_property(e),
2199            Expression::DataDeletionProperty(e) => self.generate_data_deletion_property(e),
2200            Expression::Date(e) => self.generate_date_func(e),
2201            Expression::DateBin(e) => self.generate_date_bin(e),
2202            Expression::DateFormatColumnConstraint(e) => self.generate_date_format_column_constraint(e),
2203            Expression::DateFromParts(e) => self.generate_date_from_parts(e),
2204            Expression::Datetime(e) => self.generate_datetime(e),
2205            Expression::DatetimeAdd(e) => self.generate_datetime_add(e),
2206            Expression::DatetimeDiff(e) => self.generate_datetime_diff(e),
2207            Expression::DatetimeSub(e) => self.generate_datetime_sub(e),
2208            Expression::DatetimeTrunc(e) => self.generate_datetime_trunc(e),
2209            Expression::Dayname(e) => self.generate_dayname(e),
2210            Expression::Declare(e) => self.generate_declare(e),
2211            Expression::DeclareItem(e) => self.generate_declare_item(e),
2212            Expression::DecodeCase(e) => self.generate_decode_case(e),
2213            Expression::DecompressBinary(e) => self.generate_decompress_binary(e),
2214            Expression::DecompressString(e) => self.generate_decompress_string(e),
2215            Expression::Decrypt(e) => self.generate_decrypt(e),
2216            Expression::DecryptRaw(e) => self.generate_decrypt_raw(e),
2217            Expression::DefinerProperty(e) => self.generate_definer_property(e),
2218            Expression::Detach(e) => self.generate_detach(e),
2219            Expression::DictProperty(e) => self.generate_dict_property(e),
2220            Expression::DictRange(e) => self.generate_dict_range(e),
2221            Expression::Directory(e) => self.generate_directory(e),
2222            Expression::DistKeyProperty(e) => self.generate_dist_key_property(e),
2223            Expression::DistStyleProperty(e) => self.generate_dist_style_property(e),
2224            Expression::DistributeBy(e) => self.generate_distribute_by(e),
2225            Expression::DistributedByProperty(e) => self.generate_distributed_by_property(e),
2226            Expression::DotProduct(e) => self.generate_dot_product(e),
2227            Expression::DropPartition(e) => self.generate_drop_partition(e),
2228            Expression::DuplicateKeyProperty(e) => self.generate_duplicate_key_property(e),
2229            Expression::Elt(e) => self.generate_elt(e),
2230            Expression::Encode(e) => self.generate_encode(e),
2231            Expression::EncodeProperty(e) => self.generate_encode_property(e),
2232            Expression::Encrypt(e) => self.generate_encrypt(e),
2233            Expression::EncryptRaw(e) => self.generate_encrypt_raw(e),
2234            Expression::EngineProperty(e) => self.generate_engine_property(e),
2235            Expression::EnviromentProperty(e) => self.generate_enviroment_property(e),
2236            Expression::EphemeralColumnConstraint(e) => self.generate_ephemeral_column_constraint(e),
2237            Expression::EqualNull(e) => self.generate_equal_null(e),
2238            Expression::EuclideanDistance(e) => self.generate_euclidean_distance(e),
2239            Expression::ExecuteAsProperty(e) => self.generate_execute_as_property(e),
2240            Expression::Export(e) => self.generate_export(e),
2241            Expression::ExternalProperty(e) => self.generate_external_property(e),
2242            Expression::FallbackProperty(e) => self.generate_fallback_property(e),
2243            Expression::FarmFingerprint(e) => self.generate_farm_fingerprint(e),
2244            Expression::FeaturesAtTime(e) => self.generate_features_at_time(e),
2245            Expression::Fetch(e) => self.generate_fetch(e),
2246            Expression::FileFormatProperty(e) => self.generate_file_format_property(e),
2247            Expression::Filter(e) => self.generate_filter(e),
2248            Expression::Float64(e) => self.generate_float64(e),
2249            Expression::ForIn(e) => self.generate_for_in(e),
2250            Expression::ForeignKey(e) => self.generate_foreign_key(e),
2251            Expression::Format(e) => self.generate_format(e),
2252            Expression::FormatPhrase(e) => self.generate_format_phrase(e),
2253            Expression::FreespaceProperty(e) => self.generate_freespace_property(e),
2254            Expression::From(e) => self.generate_from(e),
2255            Expression::FromBase(e) => self.generate_from_base(e),
2256            Expression::FromTimeZone(e) => self.generate_from_time_zone(e),
2257            Expression::GapFill(e) => self.generate_gap_fill(e),
2258            Expression::GenerateDateArray(e) => self.generate_generate_date_array(e),
2259            Expression::GenerateEmbedding(e) => self.generate_generate_embedding(e),
2260            Expression::GenerateSeries(e) => self.generate_generate_series(e),
2261            Expression::GenerateTimestampArray(e) => self.generate_generate_timestamp_array(e),
2262            Expression::GeneratedAsIdentityColumnConstraint(e) => self.generate_generated_as_identity_column_constraint(e),
2263            Expression::GeneratedAsRowColumnConstraint(e) => self.generate_generated_as_row_column_constraint(e),
2264            Expression::Get(e) => self.generate_get(e),
2265            Expression::GetExtract(e) => self.generate_get_extract(e),
2266            Expression::Getbit(e) => self.generate_getbit(e),
2267            Expression::GrantPrincipal(e) => self.generate_grant_principal(e),
2268            Expression::GrantPrivilege(e) => self.generate_grant_privilege(e),
2269            Expression::Group(e) => self.generate_group(e),
2270            Expression::GroupBy(e) => self.generate_group_by(e),
2271            Expression::Grouping(e) => self.generate_grouping(e),
2272            Expression::GroupingId(e) => self.generate_grouping_id(e),
2273            Expression::GroupingSets(e) => self.generate_grouping_sets(e),
2274            Expression::HashAgg(e) => self.generate_hash_agg(e),
2275            Expression::Having(e) => self.generate_having(e),
2276            Expression::HavingMax(e) => self.generate_having_max(e),
2277            Expression::Heredoc(e) => self.generate_heredoc(e),
2278            Expression::HexEncode(e) => self.generate_hex_encode(e),
2279            Expression::Hll(e) => self.generate_hll(e),
2280            Expression::InOutColumnConstraint(e) => self.generate_in_out_column_constraint(e),
2281            Expression::IncludeProperty(e) => self.generate_include_property(e),
2282            Expression::Index(e) => self.generate_index(e),
2283            Expression::IndexColumnConstraint(e) => self.generate_index_column_constraint(e),
2284            Expression::IndexConstraintOption(e) => self.generate_index_constraint_option(e),
2285            Expression::IndexParameters(e) => self.generate_index_parameters(e),
2286            Expression::IndexTableHint(e) => self.generate_index_table_hint(e),
2287            Expression::InheritsProperty(e) => self.generate_inherits_property(e),
2288            Expression::InputModelProperty(e) => self.generate_input_model_property(e),
2289            Expression::InputOutputFormat(e) => self.generate_input_output_format(e),
2290            Expression::Install(e) => self.generate_install(e),
2291            Expression::IntervalOp(e) => self.generate_interval_op(e),
2292            Expression::IntervalSpan(e) => self.generate_interval_span(e),
2293            Expression::IntoClause(e) => self.generate_into_clause(e),
2294            Expression::Introducer(e) => self.generate_introducer(e),
2295            Expression::IsolatedLoadingProperty(e) => self.generate_isolated_loading_property(e),
2296            Expression::JSON(e) => self.generate_json(e),
2297            Expression::JSONArray(e) => self.generate_json_array(e),
2298            Expression::JSONArrayAgg(e) => self.generate_json_array_agg_struct(e),
2299            Expression::JSONArrayAppend(e) => self.generate_json_array_append(e),
2300            Expression::JSONArrayContains(e) => self.generate_json_array_contains(e),
2301            Expression::JSONArrayInsert(e) => self.generate_json_array_insert(e),
2302            Expression::JSONBExists(e) => self.generate_jsonb_exists(e),
2303            Expression::JSONBExtractScalar(e) => self.generate_jsonb_extract_scalar(e),
2304            Expression::JSONBObjectAgg(e) => self.generate_jsonb_object_agg(e),
2305            Expression::JSONObjectAgg(e) => self.generate_json_object_agg_struct(e),
2306            Expression::JSONColumnDef(e) => self.generate_json_column_def(e),
2307            Expression::JSONExists(e) => self.generate_json_exists(e),
2308            Expression::JSONCast(e) => self.generate_json_cast(e),
2309            Expression::JSONExtract(e) => self.generate_json_extract_path(e),
2310            Expression::JSONExtractArray(e) => self.generate_json_extract_array(e),
2311            Expression::JSONExtractQuote(e) => self.generate_json_extract_quote(e),
2312            Expression::JSONExtractScalar(e) => self.generate_json_extract_scalar(e),
2313            Expression::JSONFormat(e) => self.generate_json_format(e),
2314            Expression::JSONKeyValue(e) => self.generate_json_key_value(e),
2315            Expression::JSONKeys(e) => self.generate_json_keys(e),
2316            Expression::JSONKeysAtDepth(e) => self.generate_json_keys_at_depth(e),
2317            Expression::JSONPath(e) => self.generate_json_path_expr(e),
2318            Expression::JSONPathFilter(e) => self.generate_json_path_filter(e),
2319            Expression::JSONPathKey(e) => self.generate_json_path_key(e),
2320            Expression::JSONPathRecursive(e) => self.generate_json_path_recursive(e),
2321            Expression::JSONPathRoot(_) => self.generate_json_path_root(),
2322            Expression::JSONPathScript(e) => self.generate_json_path_script(e),
2323            Expression::JSONPathSelector(e) => self.generate_json_path_selector(e),
2324            Expression::JSONPathSlice(e) => self.generate_json_path_slice(e),
2325            Expression::JSONPathSubscript(e) => self.generate_json_path_subscript(e),
2326            Expression::JSONPathUnion(e) => self.generate_json_path_union(e),
2327            Expression::JSONRemove(e) => self.generate_json_remove(e),
2328            Expression::JSONSchema(e) => self.generate_json_schema(e),
2329            Expression::JSONSet(e) => self.generate_json_set(e),
2330            Expression::JSONStripNulls(e) => self.generate_json_strip_nulls(e),
2331            Expression::JSONTable(e) => self.generate_json_table(e),
2332            Expression::JSONType(e) => self.generate_json_type(e),
2333            Expression::JSONValue(e) => self.generate_json_value(e),
2334            Expression::JSONValueArray(e) => self.generate_json_value_array(e),
2335            Expression::JarowinklerSimilarity(e) => self.generate_jarowinkler_similarity(e),
2336            Expression::JoinHint(e) => self.generate_join_hint(e),
2337            Expression::JournalProperty(e) => self.generate_journal_property(e),
2338            Expression::LanguageProperty(e) => self.generate_language_property(e),
2339            Expression::Lateral(e) => self.generate_lateral(e),
2340            Expression::LikeProperty(e) => self.generate_like_property(e),
2341            Expression::Limit(e) => self.generate_limit(e),
2342            Expression::LimitOptions(e) => self.generate_limit_options(e),
2343            Expression::List(e) => self.generate_list(e),
2344            Expression::ToMap(e) => self.generate_tomap(e),
2345            Expression::Localtime(e) => self.generate_localtime(e),
2346            Expression::Localtimestamp(e) => self.generate_localtimestamp(e),
2347            Expression::LocationProperty(e) => self.generate_location_property(e),
2348            Expression::Lock(e) => self.generate_lock(e),
2349            Expression::LockProperty(e) => self.generate_lock_property(e),
2350            Expression::LockingProperty(e) => self.generate_locking_property(e),
2351            Expression::LockingStatement(e) => self.generate_locking_statement(e),
2352            Expression::LogProperty(e) => self.generate_log_property(e),
2353            Expression::MD5Digest(e) => self.generate_md5_digest(e),
2354            Expression::MLForecast(e) => self.generate_ml_forecast(e),
2355            Expression::MLTranslate(e) => self.generate_ml_translate(e),
2356            Expression::MakeInterval(e) => self.generate_make_interval(e),
2357            Expression::ManhattanDistance(e) => self.generate_manhattan_distance(e),
2358            Expression::Map(e) => self.generate_map(e),
2359            Expression::MapCat(e) => self.generate_map_cat(e),
2360            Expression::MapDelete(e) => self.generate_map_delete(e),
2361            Expression::MapInsert(e) => self.generate_map_insert(e),
2362            Expression::MapPick(e) => self.generate_map_pick(e),
2363            Expression::MaskingPolicyColumnConstraint(e) => self.generate_masking_policy_column_constraint(e),
2364            Expression::MatchAgainst(e) => self.generate_match_against(e),
2365            Expression::MatchRecognizeMeasure(e) => self.generate_match_recognize_measure(e),
2366            Expression::MaterializedProperty(e) => self.generate_materialized_property(e),
2367            Expression::Merge(e) => self.generate_merge(e),
2368            Expression::MergeBlockRatioProperty(e) => self.generate_merge_block_ratio_property(e),
2369            Expression::MergeTreeTTL(e) => self.generate_merge_tree_ttl(e),
2370            Expression::MergeTreeTTLAction(e) => self.generate_merge_tree_ttl_action(e),
2371            Expression::Minhash(e) => self.generate_minhash(e),
2372            Expression::ModelAttribute(e) => self.generate_model_attribute(e),
2373            Expression::Monthname(e) => self.generate_monthname(e),
2374            Expression::MultitableInserts(e) => self.generate_multitable_inserts(e),
2375            Expression::NextValueFor(e) => self.generate_next_value_for(e),
2376            Expression::Normal(e) => self.generate_normal(e),
2377            Expression::Normalize(e) => self.generate_normalize(e),
2378            Expression::NotNullColumnConstraint(e) => self.generate_not_null_column_constraint(e),
2379            Expression::Nullif(e) => self.generate_nullif(e),
2380            Expression::NumberToStr(e) => self.generate_number_to_str(e),
2381            Expression::ObjectAgg(e) => self.generate_object_agg(e),
2382            Expression::ObjectIdentifier(e) => self.generate_object_identifier(e),
2383            Expression::ObjectInsert(e) => self.generate_object_insert(e),
2384            Expression::Offset(e) => self.generate_offset(e),
2385            Expression::Qualify(e) => self.generate_qualify(e),
2386            Expression::OnCluster(e) => self.generate_on_cluster(e),
2387            Expression::OnCommitProperty(e) => self.generate_on_commit_property(e),
2388            Expression::OnCondition(e) => self.generate_on_condition(e),
2389            Expression::OnConflict(e) => self.generate_on_conflict(e),
2390            Expression::OnProperty(e) => self.generate_on_property(e),
2391            Expression::Opclass(e) => self.generate_opclass(e),
2392            Expression::OpenJSON(e) => self.generate_open_json(e),
2393            Expression::OpenJSONColumnDef(e) => self.generate_open_json_column_def(e),
2394            Expression::Operator(e) => self.generate_operator(e),
2395            Expression::OrderBy(e) => self.generate_order_by(e),
2396            Expression::OutputModelProperty(e) => self.generate_output_model_property(e),
2397            Expression::OverflowTruncateBehavior(e) => self.generate_overflow_truncate_behavior(e),
2398            Expression::ParameterizedAgg(e) => self.generate_parameterized_agg(e),
2399            Expression::ParseDatetime(e) => self.generate_parse_datetime(e),
2400            Expression::ParseIp(e) => self.generate_parse_ip(e),
2401            Expression::ParseJSON(e) => self.generate_parse_json(e),
2402            Expression::ParseTime(e) => self.generate_parse_time(e),
2403            Expression::ParseUrl(e) => self.generate_parse_url(e),
2404            Expression::Partition(e) => self.generate_partition_expr(e),
2405            Expression::PartitionBoundSpec(e) => self.generate_partition_bound_spec(e),
2406            Expression::PartitionByListProperty(e) => self.generate_partition_by_list_property(e),
2407            Expression::PartitionByRangeProperty(e) => self.generate_partition_by_range_property(e),
2408            Expression::PartitionByRangePropertyDynamic(e) => self.generate_partition_by_range_property_dynamic(e),
2409            Expression::PartitionByTruncate(e) => self.generate_partition_by_truncate(e),
2410            Expression::PartitionList(e) => self.generate_partition_list(e),
2411            Expression::PartitionRange(e) => self.generate_partition_range(e),
2412            Expression::PartitionedByBucket(e) => self.generate_partitioned_by_bucket(e),
2413            Expression::PartitionedByProperty(e) => self.generate_partitioned_by_property(e),
2414            Expression::PartitionedOfProperty(e) => self.generate_partitioned_of_property(e),
2415            Expression::PeriodForSystemTimeConstraint(e) => self.generate_period_for_system_time_constraint(e),
2416            Expression::PivotAlias(e) => self.generate_pivot_alias(e),
2417            Expression::PivotAny(e) => self.generate_pivot_any(e),
2418            Expression::Predict(e) => self.generate_predict(e),
2419            Expression::PreviousDay(e) => self.generate_previous_day(e),
2420            Expression::PrimaryKey(e) => self.generate_primary_key(e),
2421            Expression::PrimaryKeyColumnConstraint(e) => self.generate_primary_key_column_constraint(e),
2422            Expression::PathColumnConstraint(e) => self.generate_path_column_constraint(e),
2423            Expression::ProjectionDef(e) => self.generate_projection_def(e),
2424            Expression::Properties(e) => self.generate_properties(e),
2425            Expression::Property(e) => self.generate_property(e),
2426            Expression::PseudoType(e) => self.generate_pseudo_type(e),
2427            Expression::Put(e) => self.generate_put(e),
2428            Expression::Quantile(e) => self.generate_quantile(e),
2429            Expression::QueryBand(e) => self.generate_query_band(e),
2430            Expression::QueryOption(e) => self.generate_query_option(e),
2431            Expression::QueryTransform(e) => self.generate_query_transform(e),
2432            Expression::Randn(e) => self.generate_randn(e),
2433            Expression::Randstr(e) => self.generate_randstr(e),
2434            Expression::RangeBucket(e) => self.generate_range_bucket(e),
2435            Expression::RangeN(e) => self.generate_range_n(e),
2436            Expression::ReadCSV(e) => self.generate_read_csv(e),
2437            Expression::ReadParquet(e) => self.generate_read_parquet(e),
2438            Expression::RecursiveWithSearch(e) => self.generate_recursive_with_search(e),
2439            Expression::Reduce(e) => self.generate_reduce(e),
2440            Expression::Reference(e) => self.generate_reference(e),
2441            Expression::Refresh(e) => self.generate_refresh(e),
2442            Expression::RefreshTriggerProperty(e) => self.generate_refresh_trigger_property(e),
2443            Expression::RegexpCount(e) => self.generate_regexp_count(e),
2444            Expression::RegexpExtractAll(e) => self.generate_regexp_extract_all(e),
2445            Expression::RegexpFullMatch(e) => self.generate_regexp_full_match(e),
2446            Expression::RegexpILike(e) => self.generate_regexp_i_like(e),
2447            Expression::RegexpInstr(e) => self.generate_regexp_instr(e),
2448            Expression::RegexpSplit(e) => self.generate_regexp_split(e),
2449            Expression::RegrAvgx(e) => self.generate_regr_avgx(e),
2450            Expression::RegrAvgy(e) => self.generate_regr_avgy(e),
2451            Expression::RegrCount(e) => self.generate_regr_count(e),
2452            Expression::RegrIntercept(e) => self.generate_regr_intercept(e),
2453            Expression::RegrR2(e) => self.generate_regr_r2(e),
2454            Expression::RegrSlope(e) => self.generate_regr_slope(e),
2455            Expression::RegrSxx(e) => self.generate_regr_sxx(e),
2456            Expression::RegrSxy(e) => self.generate_regr_sxy(e),
2457            Expression::RegrSyy(e) => self.generate_regr_syy(e),
2458            Expression::RegrValx(e) => self.generate_regr_valx(e),
2459            Expression::RegrValy(e) => self.generate_regr_valy(e),
2460            Expression::RemoteWithConnectionModelProperty(e) => self.generate_remote_with_connection_model_property(e),
2461            Expression::RenameColumn(e) => self.generate_rename_column(e),
2462            Expression::ReplacePartition(e) => self.generate_replace_partition(e),
2463            Expression::Returning(e) => self.generate_returning(e),
2464            Expression::ReturnsProperty(e) => self.generate_returns_property(e),
2465            Expression::Rollback(e) => self.generate_rollback(e),
2466            Expression::Rollup(e) => self.generate_rollup(e),
2467            Expression::RowFormatDelimitedProperty(e) => self.generate_row_format_delimited_property(e),
2468            Expression::RowFormatProperty(e) => self.generate_row_format_property(e),
2469            Expression::RowFormatSerdeProperty(e) => self.generate_row_format_serde_property(e),
2470            Expression::SHA2(e) => self.generate_sha2(e),
2471            Expression::SHA2Digest(e) => self.generate_sha2_digest(e),
2472            Expression::SafeAdd(e) => self.generate_safe_add(e),
2473            Expression::SafeDivide(e) => self.generate_safe_divide(e),
2474            Expression::SafeMultiply(e) => self.generate_safe_multiply(e),
2475            Expression::SafeSubtract(e) => self.generate_safe_subtract(e),
2476            Expression::SampleProperty(e) => self.generate_sample_property(e),
2477            Expression::Schema(e) => self.generate_schema(e),
2478            Expression::SchemaCommentProperty(e) => self.generate_schema_comment_property(e),
2479            Expression::ScopeResolution(e) => self.generate_scope_resolution(e),
2480            Expression::Search(e) => self.generate_search(e),
2481            Expression::SearchIp(e) => self.generate_search_ip(e),
2482            Expression::SecurityProperty(e) => self.generate_security_property(e),
2483            Expression::SemanticView(e) => self.generate_semantic_view(e),
2484            Expression::SequenceProperties(e) => self.generate_sequence_properties(e),
2485            Expression::SerdeProperties(e) => self.generate_serde_properties(e),
2486            Expression::SessionParameter(e) => self.generate_session_parameter(e),
2487            Expression::Set(e) => self.generate_set(e),
2488            Expression::SetConfigProperty(e) => self.generate_set_config_property(e),
2489            Expression::SetItem(e) => self.generate_set_item(e),
2490            Expression::SetOperation(e) => self.generate_set_operation(e),
2491            Expression::SetProperty(e) => self.generate_set_property(e),
2492            Expression::SettingsProperty(e) => self.generate_settings_property(e),
2493            Expression::SharingProperty(e) => self.generate_sharing_property(e),
2494            Expression::Slice(e) => self.generate_slice(e),
2495            Expression::SortArray(e) => self.generate_sort_array(e),
2496            Expression::SortBy(e) => self.generate_sort_by(e),
2497            Expression::SortKeyProperty(e) => self.generate_sort_key_property(e),
2498            Expression::SplitPart(e) => self.generate_split_part(e),
2499            Expression::SqlReadWriteProperty(e) => self.generate_sql_read_write_property(e),
2500            Expression::SqlSecurityProperty(e) => self.generate_sql_security_property(e),
2501            Expression::StDistance(e) => self.generate_st_distance(e),
2502            Expression::StPoint(e) => self.generate_st_point(e),
2503            Expression::StabilityProperty(e) => self.generate_stability_property(e),
2504            Expression::StandardHash(e) => self.generate_standard_hash(e),
2505            Expression::StorageHandlerProperty(e) => self.generate_storage_handler_property(e),
2506            Expression::StrPosition(e) => self.generate_str_position(e),
2507            Expression::StrToDate(e) => self.generate_str_to_date(e),
2508            Expression::DateStrToDate(f) => self.generate_simple_func("DATE_STR_TO_DATE", &f.this),
2509            Expression::DateToDateStr(f) => self.generate_simple_func("DATE_TO_DATE_STR", &f.this),
2510            Expression::StrToMap(e) => self.generate_str_to_map(e),
2511            Expression::StrToTime(e) => self.generate_str_to_time(e),
2512            Expression::StrToUnix(e) => self.generate_str_to_unix(e),
2513            Expression::StringToArray(e) => self.generate_string_to_array(e),
2514            Expression::Struct(e) => self.generate_struct(e),
2515            Expression::Stuff(e) => self.generate_stuff(e),
2516            Expression::SubstringIndex(e) => self.generate_substring_index(e),
2517            Expression::Summarize(e) => self.generate_summarize(e),
2518            Expression::Systimestamp(e) => self.generate_systimestamp(e),
2519            Expression::TableAlias(e) => self.generate_table_alias(e),
2520            Expression::TableFromRows(e) => self.generate_table_from_rows(e),
2521            Expression::RowsFrom(e) => self.generate_rows_from(e),
2522            Expression::TableSample(e) => self.generate_table_sample(e),
2523            Expression::Tag(e) => self.generate_tag(e),
2524            Expression::Tags(e) => self.generate_tags(e),
2525            Expression::TemporaryProperty(e) => self.generate_temporary_property(e),
2526            Expression::Time(e) => self.generate_time_func(e),
2527            Expression::TimeAdd(e) => self.generate_time_add(e),
2528            Expression::TimeDiff(e) => self.generate_time_diff(e),
2529            Expression::TimeFromParts(e) => self.generate_time_from_parts(e),
2530            Expression::TimeSlice(e) => self.generate_time_slice(e),
2531            Expression::TimeStrToTime(e) => self.generate_time_str_to_time(e),
2532            Expression::TimeSub(e) => self.generate_time_sub(e),
2533            Expression::TimeToStr(e) => self.generate_time_to_str(e),
2534            Expression::TimeTrunc(e) => self.generate_time_trunc(e),
2535            Expression::TimeUnit(e) => self.generate_time_unit(e),
2536            Expression::Timestamp(e) => self.generate_timestamp_func(e),
2537            Expression::TimestampAdd(e) => self.generate_timestamp_add(e),
2538            Expression::TimestampDiff(e) => self.generate_timestamp_diff(e),
2539            Expression::TimestampFromParts(e) => self.generate_timestamp_from_parts(e),
2540            Expression::TimestampSub(e) => self.generate_timestamp_sub(e),
2541            Expression::TimestampTzFromParts(e) => self.generate_timestamp_tz_from_parts(e),
2542            Expression::ToBinary(e) => self.generate_to_binary(e),
2543            Expression::ToBoolean(e) => self.generate_to_boolean(e),
2544            Expression::ToChar(e) => self.generate_to_char(e),
2545            Expression::ToDecfloat(e) => self.generate_to_decfloat(e),
2546            Expression::ToDouble(e) => self.generate_to_double(e),
2547            Expression::ToFile(e) => self.generate_to_file(e),
2548            Expression::ToNumber(e) => self.generate_to_number(e),
2549            Expression::ToTableProperty(e) => self.generate_to_table_property(e),
2550            Expression::Transaction(e) => self.generate_transaction(e),
2551            Expression::Transform(e) => self.generate_transform(e),
2552            Expression::TransformModelProperty(e) => self.generate_transform_model_property(e),
2553            Expression::TransientProperty(e) => self.generate_transient_property(e),
2554            Expression::Translate(e) => self.generate_translate(e),
2555            Expression::TranslateCharacters(e) => self.generate_translate_characters(e),
2556            Expression::TruncateTable(e) => self.generate_truncate_table(e),
2557            Expression::TryBase64DecodeBinary(e) => self.generate_try_base64_decode_binary(e),
2558            Expression::TryBase64DecodeString(e) => self.generate_try_base64_decode_string(e),
2559            Expression::TryToDecfloat(e) => self.generate_try_to_decfloat(e),
2560            Expression::TsOrDsAdd(e) => self.generate_ts_or_ds_add(e),
2561            Expression::TsOrDsDiff(e) => self.generate_ts_or_ds_diff(e),
2562            Expression::TsOrDsToDate(e) => self.generate_ts_or_ds_to_date(e),
2563            Expression::TsOrDsToTime(e) => self.generate_ts_or_ds_to_time(e),
2564            Expression::Unhex(e) => self.generate_unhex(e),
2565            Expression::UnicodeString(e) => self.generate_unicode_string(e),
2566            Expression::Uniform(e) => self.generate_uniform(e),
2567            Expression::UniqueColumnConstraint(e) => self.generate_unique_column_constraint(e),
2568            Expression::UniqueKeyProperty(e) => self.generate_unique_key_property(e),
2569            Expression::RollupProperty(e) => self.generate_rollup_property(e),
2570            Expression::UnixToStr(e) => self.generate_unix_to_str(e),
2571            Expression::UnixToTime(e) => self.generate_unix_to_time(e),
2572            Expression::UnpivotColumns(e) => self.generate_unpivot_columns(e),
2573            Expression::UserDefinedFunction(e) => self.generate_user_defined_function(e),
2574            Expression::UsingTemplateProperty(e) => self.generate_using_template_property(e),
2575            Expression::UtcTime(e) => self.generate_utc_time(e),
2576            Expression::UtcTimestamp(e) => self.generate_utc_timestamp(e),
2577            Expression::Uuid(e) => self.generate_uuid(e),
2578            Expression::Var(v) => {
2579                if matches!(self.config.dialect, Some(DialectType::MySQL))
2580                    && v.this.len() > 2
2581                    && (v.this.starts_with("0x") || v.this.starts_with("0X"))
2582                    && !v.this[2..].chars().all(|c| c.is_ascii_hexdigit())
2583                {
2584                    return self.generate_identifier(&Identifier {
2585                        name: v.this.clone(),
2586                        quoted: true,
2587                        trailing_comments: Vec::new(),
2588                    });
2589                }
2590                self.write(&v.this);
2591                Ok(())
2592            }
2593            Expression::VarMap(e) => self.generate_var_map(e),
2594            Expression::VectorSearch(e) => self.generate_vector_search(e),
2595            Expression::Version(e) => self.generate_version(e),
2596            Expression::ViewAttributeProperty(e) => self.generate_view_attribute_property(e),
2597            Expression::VolatileProperty(e) => self.generate_volatile_property(e),
2598            Expression::WatermarkColumnConstraint(e) => self.generate_watermark_column_constraint(e),
2599            Expression::Week(e) => self.generate_week(e),
2600            Expression::When(e) => self.generate_when(e),
2601            Expression::Whens(e) => self.generate_whens(e),
2602            Expression::Where(e) => self.generate_where(e),
2603            Expression::WidthBucket(e) => self.generate_width_bucket(e),
2604            Expression::Window(e) => self.generate_window(e),
2605            Expression::WindowSpec(e) => self.generate_window_spec(e),
2606            Expression::WithDataProperty(e) => self.generate_with_data_property(e),
2607            Expression::WithFill(e) => self.generate_with_fill(e),
2608            Expression::WithJournalTableProperty(e) => self.generate_with_journal_table_property(e),
2609            Expression::WithOperator(e) => self.generate_with_operator(e),
2610            Expression::WithProcedureOptions(e) => self.generate_with_procedure_options(e),
2611            Expression::WithSchemaBindingProperty(e) => self.generate_with_schema_binding_property(e),
2612            Expression::WithSystemVersioningProperty(e) => self.generate_with_system_versioning_property(e),
2613            Expression::WithTableHint(e) => self.generate_with_table_hint(e),
2614            Expression::XMLElement(e) => self.generate_xml_element(e),
2615            Expression::XMLGet(e) => self.generate_xml_get(e),
2616            Expression::XMLKeyValueOption(e) => self.generate_xml_key_value_option(e),
2617            Expression::XMLTable(e) => self.generate_xml_table(e),
2618            Expression::Xor(e) => self.generate_xor(e),
2619            Expression::Zipf(e) => self.generate_zipf(e),
2620            _ => {
2621                // Fallback for unimplemented expressions
2622                self.write(&format!("/* unimplemented: {:?} */", expr));
2623                Ok(())
2624            }
2625        }
2626    }
2627
2628    fn generate_select(&mut self, select: &Select) -> Result<()> {
2629        use crate::dialects::DialectType;
2630
2631        // Output leading comments before SELECT
2632        for comment in &select.leading_comments {
2633            self.write_formatted_comment(comment);
2634            self.write(" ");
2635        }
2636
2637        // WITH clause
2638        if let Some(with) = &select.with {
2639            self.generate_with(with)?;
2640            if self.config.pretty {
2641                self.write_newline();
2642                self.write_indent();
2643            } else {
2644                self.write_space();
2645            }
2646        }
2647
2648        // Output post-SELECT comments (comments that appeared after SELECT keyword)
2649        // These are output BEFORE SELECT, as Python SQLGlot normalizes them this way
2650        for comment in &select.post_select_comments {
2651            self.write_formatted_comment(comment);
2652            self.write(" ");
2653        }
2654
2655        self.write_keyword("SELECT");
2656
2657        // Generate query hint if present /*+ ... */
2658        if let Some(hint) = &select.hint {
2659            self.generate_hint(hint)?;
2660        }
2661
2662        // For SQL Server, convert LIMIT to TOP (structural transformation)
2663        // But only when there's no OFFSET (otherwise use OFFSET/FETCH syntax)
2664        // TOP clause (SQL Server style - before DISTINCT)
2665        let use_top_from_limit = matches!(self.config.dialect, Some(DialectType::TSQL))
2666            && select.top.is_none()
2667            && select.limit.is_some()
2668            && select.offset.is_none();  // Don't use TOP when there's OFFSET
2669
2670        // For TOP-supporting dialects: DISTINCT before TOP
2671        // For non-TOP dialects: TOP is converted to LIMIT later; DISTINCT goes here
2672        let is_top_dialect = matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Teradata) | Some(DialectType::Fabric));
2673
2674        if select.distinct && (is_top_dialect || select.top.is_some()) {
2675            self.write_space();
2676            self.write_keyword("DISTINCT");
2677        }
2678
2679        if is_top_dialect {
2680            if let Some(top) = &select.top {
2681                self.write_space();
2682                self.write_keyword("TOP");
2683                if top.parenthesized {
2684                    self.write(" (");
2685                    self.generate_expression(&top.this)?;
2686                    self.write(")");
2687                } else {
2688                    self.write_space();
2689                    self.generate_expression(&top.this)?;
2690                }
2691                if top.percent {
2692                    self.write_space();
2693                    self.write_keyword("PERCENT");
2694                }
2695                if top.with_ties {
2696                    self.write_space();
2697                    self.write_keyword("WITH TIES");
2698                }
2699            } else if use_top_from_limit {
2700                // Convert LIMIT to TOP for SQL Server (only when no OFFSET)
2701                if let Some(limit) = &select.limit {
2702                    self.write_space();
2703                    self.write_keyword("TOP");
2704                    // Use parentheses for complex expressions, but not for simple literals
2705                    let is_simple_literal = matches!(&limit.this, Expression::Literal(Literal::Number(_)));
2706                    if is_simple_literal {
2707                        self.write_space();
2708                        self.generate_expression(&limit.this)?;
2709                    } else {
2710                        self.write(" (");
2711                        self.generate_expression(&limit.this)?;
2712                        self.write(")");
2713                    }
2714                }
2715            }
2716        }
2717
2718        if select.distinct && !is_top_dialect && select.top.is_none() {
2719            self.write_space();
2720            self.write_keyword("DISTINCT");
2721        }
2722
2723        // DISTINCT ON clause (PostgreSQL)
2724        if let Some(distinct_on) = &select.distinct_on {
2725            self.write_space();
2726            self.write_keyword("ON");
2727            self.write(" (");
2728            for (i, expr) in distinct_on.iter().enumerate() {
2729                if i > 0 {
2730                    self.write(", ");
2731                }
2732                self.generate_expression(expr)?;
2733            }
2734            self.write(")");
2735        }
2736
2737        // MySQL operation modifiers (HIGH_PRIORITY, STRAIGHT_JOIN, SQL_CALC_FOUND_ROWS, etc.)
2738        for modifier in &select.operation_modifiers {
2739            self.write_space();
2740            self.write_keyword(modifier);
2741        }
2742
2743        // BigQuery SELECT AS STRUCT / SELECT AS VALUE
2744        if let Some(kind) = &select.kind {
2745            self.write_space();
2746            self.write_keyword("AS");
2747            self.write_space();
2748            self.write_keyword(kind);
2749        }
2750
2751        // Expressions (only if there are any)
2752        if !select.expressions.is_empty() {
2753            if self.config.pretty {
2754                self.write_newline();
2755                self.indent_level += 1;
2756            } else {
2757                self.write_space();
2758            }
2759        }
2760
2761        for (i, expr) in select.expressions.iter().enumerate() {
2762            if i > 0 {
2763                self.write(",");
2764                if self.config.pretty {
2765                    self.write_newline();
2766                } else {
2767                    self.write_space();
2768                }
2769            }
2770            if self.config.pretty {
2771                self.write_indent();
2772            }
2773            self.generate_expression(expr)?;
2774        }
2775
2776        if self.config.pretty && !select.expressions.is_empty() {
2777            self.indent_level -= 1;
2778        }
2779
2780        // INTO clause (SELECT ... INTO table_name)
2781        // Also handles Oracle PL/SQL: BULK COLLECT INTO v1, v2, ...
2782        if let Some(into) = &select.into {
2783            if self.config.pretty {
2784                self.write_newline();
2785                self.write_indent();
2786            } else {
2787                self.write_space();
2788            }
2789            if into.bulk_collect {
2790                self.write_keyword("BULK COLLECT INTO");
2791            } else {
2792                self.write_keyword("INTO");
2793            }
2794            if into.temporary {
2795                self.write_space();
2796                self.write_keyword("TEMPORARY");
2797            }
2798            if into.unlogged {
2799                self.write_space();
2800                self.write_keyword("UNLOGGED");
2801            }
2802            self.write_space();
2803            // If we have multiple expressions, output them comma-separated
2804            if !into.expressions.is_empty() {
2805                for (i, expr) in into.expressions.iter().enumerate() {
2806                    if i > 0 {
2807                        self.write(", ");
2808                    }
2809                    self.generate_expression(expr)?;
2810                }
2811            } else {
2812                self.generate_expression(&into.this)?;
2813            }
2814        }
2815
2816        // FROM clause
2817        if let Some(from) = &select.from {
2818            if self.config.pretty {
2819                self.write_newline();
2820                self.write_indent();
2821            } else {
2822                self.write_space();
2823            }
2824            self.write_keyword("FROM");
2825            self.write_space();
2826
2827            // BigQuery, Hive, Spark, Databricks, SQLite, and ClickHouse prefer explicit CROSS JOIN over comma syntax for multiple tables
2828            // But keep commas when TABLESAMPLE is present (Spark/Hive handle TABLESAMPLE differently with commas)
2829            let has_tablesample = from.expressions.iter().any(|e| matches!(e, Expression::TableSample(_)));
2830            let use_cross_join = !has_tablesample && matches!(
2831                self.config.dialect,
2832                Some(DialectType::BigQuery)
2833                    | Some(DialectType::Hive)
2834                    | Some(DialectType::Spark)
2835                    | Some(DialectType::Databricks)
2836                    | Some(DialectType::SQLite)
2837                    | Some(DialectType::ClickHouse)
2838            );
2839
2840            // Snowflake wraps standalone VALUES in FROM clause with parentheses
2841            let wrap_values_in_parens = matches!(
2842                self.config.dialect,
2843                Some(DialectType::Snowflake)
2844            );
2845
2846            for (i, expr) in from.expressions.iter().enumerate() {
2847                if i > 0 {
2848                    if use_cross_join {
2849                        self.write(" CROSS JOIN ");
2850                    } else {
2851                        self.write(", ");
2852                    }
2853                }
2854                if wrap_values_in_parens && matches!(expr, Expression::Values(_)) {
2855                    self.write("(");
2856                    self.generate_expression(expr)?;
2857                    self.write(")");
2858                } else {
2859                    self.generate_expression(expr)?;
2860                }
2861            }
2862        }
2863
2864        // JOINs
2865        for join in &select.joins {
2866            self.generate_join(join)?;
2867        }
2868
2869        // Output deferred ON/USING conditions (right-to-left, which is reverse order)
2870        // These are joins where the condition was specified after all JOINs
2871        for join in select.joins.iter().rev() {
2872            if join.deferred_condition {
2873                self.generate_join_condition(join)?;
2874            }
2875        }
2876
2877        // LATERAL VIEW clauses (Hive/Spark)
2878        for lateral_view in &select.lateral_views {
2879            self.generate_lateral_view(lateral_view)?;
2880        }
2881
2882        // PREWHERE (ClickHouse)
2883        if let Some(prewhere) = &select.prewhere {
2884            self.write_clause_condition("PREWHERE", prewhere)?;
2885        }
2886
2887        // WHERE
2888        if let Some(where_clause) = &select.where_clause {
2889            self.write_clause_condition("WHERE", &where_clause.this)?;
2890        }
2891
2892        // CONNECT BY (Oracle hierarchical queries)
2893        if let Some(connect) = &select.connect {
2894            self.generate_connect(connect)?;
2895        }
2896
2897        // GROUP BY
2898        if let Some(group_by) = &select.group_by {
2899            if self.config.pretty {
2900                self.write_newline();
2901                self.write_indent();
2902            } else {
2903                self.write_space();
2904            }
2905            self.write_keyword("GROUP BY");
2906            // Handle ALL/DISTINCT modifier: Some(true) = ALL, Some(false) = DISTINCT
2907            match group_by.all {
2908                Some(true) => {
2909                    self.write_space();
2910                    self.write_keyword("ALL");
2911                }
2912                Some(false) => {
2913                    self.write_space();
2914                    self.write_keyword("DISTINCT");
2915                }
2916                None => {}
2917            }
2918            if !group_by.expressions.is_empty() {
2919                // Check for trailing WITH CUBE or WITH ROLLUP (Hive/MySQL syntax)
2920                // These are represented as Cube/Rollup expressions with empty expressions at the end
2921                let mut trailing_cube = false;
2922                let mut trailing_rollup = false;
2923                let mut plain_expressions: Vec<&Expression> = Vec::new();
2924                let mut grouping_sets_expressions: Vec<&Expression> = Vec::new();
2925                let mut cube_expressions: Vec<&Expression> = Vec::new();
2926                let mut rollup_expressions: Vec<&Expression> = Vec::new();
2927
2928                for expr in &group_by.expressions {
2929                    match expr {
2930                        Expression::Cube(c) if c.expressions.is_empty() => {
2931                            trailing_cube = true;
2932                        }
2933                        Expression::Rollup(r) if r.expressions.is_empty() => {
2934                            trailing_rollup = true;
2935                        }
2936                        Expression::Function(f) if f.name == "CUBE" => {
2937                            cube_expressions.push(expr);
2938                        }
2939                        Expression::Function(f) if f.name == "ROLLUP" => {
2940                            rollup_expressions.push(expr);
2941                        }
2942                        Expression::Function(f) if f.name == "GROUPING SETS" => {
2943                            grouping_sets_expressions.push(expr);
2944                        }
2945                        _ => {
2946                            plain_expressions.push(expr);
2947                        }
2948                    }
2949                }
2950
2951                // Reorder: plain expressions first, then GROUPING SETS, CUBE, ROLLUP
2952                let mut regular_expressions: Vec<&Expression> = Vec::new();
2953                regular_expressions.extend(plain_expressions);
2954                regular_expressions.extend(grouping_sets_expressions);
2955                regular_expressions.extend(cube_expressions);
2956                regular_expressions.extend(rollup_expressions);
2957
2958                if self.config.pretty {
2959                    self.write_newline();
2960                    self.indent_level += 1;
2961                    self.write_indent();
2962                } else {
2963                    self.write_space();
2964                }
2965
2966                for (i, expr) in regular_expressions.iter().enumerate() {
2967                    if i > 0 {
2968                        self.write(", ");
2969                    }
2970                    self.generate_expression(expr)?;
2971                }
2972
2973                if self.config.pretty {
2974                    self.indent_level -= 1;
2975                }
2976
2977                // Output trailing WITH CUBE or WITH ROLLUP
2978                if trailing_cube {
2979                    self.write_space();
2980                    self.write_keyword("WITH CUBE");
2981                } else if trailing_rollup {
2982                    self.write_space();
2983                    self.write_keyword("WITH ROLLUP");
2984                }
2985            }
2986
2987            // ClickHouse: WITH TOTALS
2988            if group_by.totals {
2989                self.write_space();
2990                self.write_keyword("WITH TOTALS");
2991            }
2992        }
2993
2994        // HAVING
2995        if let Some(having) = &select.having {
2996            self.write_clause_condition("HAVING", &having.this)?;
2997        }
2998
2999        // QUALIFY and WINDOW clause ordering depends on input SQL
3000        if select.qualify_after_window {
3001            // WINDOW before QUALIFY (DuckDB style)
3002            if let Some(windows) = &select.windows {
3003                self.write_window_clause(windows)?;
3004            }
3005            if let Some(qualify) = &select.qualify {
3006                self.write_clause_condition("QUALIFY", &qualify.this)?;
3007            }
3008        } else {
3009            // QUALIFY before WINDOW (Snowflake/BigQuery default)
3010            if let Some(qualify) = &select.qualify {
3011                self.write_clause_condition("QUALIFY", &qualify.this)?;
3012            }
3013            if let Some(windows) = &select.windows {
3014                self.write_window_clause(windows)?;
3015            }
3016        }
3017
3018        // DISTRIBUTE BY (Hive/Spark)
3019        if let Some(distribute_by) = &select.distribute_by {
3020            self.write_clause_expressions("DISTRIBUTE BY", &distribute_by.expressions)?;
3021        }
3022
3023        // CLUSTER BY (Hive/Spark)
3024        if let Some(cluster_by) = &select.cluster_by {
3025            self.write_order_clause("CLUSTER BY", &cluster_by.expressions)?;
3026        }
3027
3028        // SORT BY (Hive/Spark - comes before ORDER BY)
3029        if let Some(sort_by) = &select.sort_by {
3030            self.write_order_clause("SORT BY", &sort_by.expressions)?;
3031        }
3032
3033        // ORDER BY (or ORDER SIBLINGS BY for Oracle hierarchical queries)
3034        if let Some(order_by) = &select.order_by {
3035            let keyword = if order_by.siblings { "ORDER SIBLINGS BY" } else { "ORDER BY" };
3036            self.write_order_clause(keyword, &order_by.expressions)?;
3037        }
3038
3039        // TSQL: FETCH requires ORDER BY. If there's a FETCH but no ORDER BY, add ORDER BY (SELECT NULL) OFFSET 0 ROWS
3040        if select.order_by.is_none() && select.fetch.is_some()
3041            && matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric))
3042        {
3043            if self.config.pretty {
3044                self.write_newline();
3045                self.write_indent();
3046            } else {
3047                self.write_space();
3048            }
3049            self.write_keyword("ORDER BY (SELECT NULL) OFFSET 0 ROWS");
3050        }
3051
3052        // LIMIT and OFFSET
3053        // PostgreSQL and others use: LIMIT count OFFSET offset
3054        // SQL Server uses: OFFSET ... FETCH (no LIMIT)
3055        // Presto/Trino uses: OFFSET n LIMIT m (offset before limit)
3056        let is_presto_like = matches!(
3057            self.config.dialect,
3058            Some(DialectType::Presto) | Some(DialectType::Trino)
3059        );
3060
3061        if is_presto_like && select.offset.is_some() {
3062            // Presto/Trino syntax: OFFSET n LIMIT m (offset comes first)
3063            if let Some(offset) = &select.offset {
3064                if self.config.pretty {
3065                    self.write_newline();
3066                    self.write_indent();
3067                } else {
3068                    self.write_space();
3069                }
3070                self.write_keyword("OFFSET");
3071                self.write_space();
3072                self.write_limit_expr(&offset.this)?;
3073                if offset.rows == Some(true) {
3074                    self.write_space();
3075                    self.write_keyword("ROWS");
3076                }
3077            }
3078            if let Some(limit) = &select.limit {
3079                if self.config.pretty {
3080                    self.write_newline();
3081                    self.write_indent();
3082                } else {
3083                    self.write_space();
3084                }
3085                self.write_keyword("LIMIT");
3086                self.write_space();
3087                self.write_limit_expr(&limit.this)?;
3088                if limit.percent {
3089                    self.write_space();
3090                    self.write_keyword("PERCENT");
3091                }
3092            }
3093        } else {
3094            // Check if FETCH will be converted to LIMIT (used for ordering)
3095            let fetch_as_limit = select.fetch.as_ref().map_or(false, |fetch| {
3096                !fetch.percent && !fetch.with_ties && fetch.count.is_some() && matches!(
3097                    self.config.dialect,
3098                    Some(DialectType::Spark) | Some(DialectType::Hive)
3099                    | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
3100                    | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
3101                    | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
3102                    | Some(DialectType::Redshift)
3103                )
3104            });
3105
3106            // Standard LIMIT clause (skip for SQL Server - we use TOP or OFFSET/FETCH instead)
3107            if let Some(limit) = &select.limit {
3108                // SQL Server uses TOP (no OFFSET) or OFFSET/FETCH (with OFFSET) instead of LIMIT
3109                if !matches!(self.config.dialect, Some(DialectType::TSQL)) {
3110                    if self.config.pretty {
3111                        self.write_newline();
3112                        self.write_indent();
3113                    } else {
3114                        self.write_space();
3115                    }
3116                    self.write_keyword("LIMIT");
3117                    self.write_space();
3118                    self.write_limit_expr(&limit.this)?;
3119                    if limit.percent {
3120                        self.write_space();
3121                        self.write_keyword("PERCENT");
3122                    }
3123                }
3124            }
3125
3126            // Convert TOP to LIMIT for non-TOP dialects
3127            if select.top.is_some() && !is_top_dialect && select.limit.is_none() {
3128                if let Some(top) = &select.top {
3129                    if !top.percent && !top.with_ties {
3130                        if self.config.pretty {
3131                            self.write_newline();
3132                            self.write_indent();
3133                        } else {
3134                            self.write_space();
3135                        }
3136                        self.write_keyword("LIMIT");
3137                        self.write_space();
3138                        self.generate_expression(&top.this)?;
3139                    }
3140                }
3141            }
3142
3143            // If FETCH will be converted to LIMIT and there's also OFFSET,
3144            // emit LIMIT from FETCH BEFORE the OFFSET
3145            if fetch_as_limit && select.offset.is_some() {
3146                if let Some(fetch) = &select.fetch {
3147                    if self.config.pretty {
3148                        self.write_newline();
3149                        self.write_indent();
3150                    } else {
3151                        self.write_space();
3152                    }
3153                    self.write_keyword("LIMIT");
3154                    self.write_space();
3155                    self.generate_expression(fetch.count.as_ref().unwrap())?;
3156                }
3157            }
3158
3159            // OFFSET
3160            // In SQL Server, OFFSET requires ORDER BY and uses different syntax
3161            // OFFSET x ROWS FETCH NEXT y ROWS ONLY
3162            if let Some(offset) = &select.offset {
3163                if self.config.pretty {
3164                    self.write_newline();
3165                    self.write_indent();
3166                } else {
3167                    self.write_space();
3168                }
3169                if matches!(self.config.dialect, Some(DialectType::TSQL)) {
3170                    // SQL Server 2012+ OFFSET ... FETCH syntax
3171                    self.write_keyword("OFFSET");
3172                    self.write_space();
3173                    self.write_limit_expr(&offset.this)?;
3174                    self.write_space();
3175                    self.write_keyword("ROWS");
3176                    // If there was a LIMIT, use FETCH NEXT ... ROWS ONLY
3177                    if let Some(limit) = &select.limit {
3178                        self.write_space();
3179                        self.write_keyword("FETCH NEXT");
3180                        self.write_space();
3181                        self.write_limit_expr(&limit.this)?;
3182                        self.write_space();
3183                        self.write_keyword("ROWS ONLY");
3184                    }
3185                } else {
3186                    self.write_keyword("OFFSET");
3187                    self.write_space();
3188                    self.write_limit_expr(&offset.this)?;
3189                    // Output ROWS keyword if it was in the original SQL
3190                    if offset.rows == Some(true) {
3191                        self.write_space();
3192                        self.write_keyword("ROWS");
3193                    }
3194                }
3195            }
3196        }
3197
3198        // ClickHouse LIMIT BY clause (after LIMIT/OFFSET)
3199        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
3200            if let Some(limit_by) = &select.limit_by {
3201                if !limit_by.is_empty() {
3202                    self.write_space();
3203                    self.write_keyword("BY");
3204                    self.write_space();
3205                    for (i, expr) in limit_by.iter().enumerate() {
3206                        if i > 0 {
3207                            self.write(", ");
3208                        }
3209                        self.generate_expression(expr)?;
3210                    }
3211                }
3212            }
3213        }
3214
3215        // ClickHouse SETTINGS and FORMAT modifiers (after LIMIT/OFFSET)
3216        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
3217            if let Some(settings) = &select.settings {
3218                if self.config.pretty {
3219                    self.write_newline();
3220                    self.write_indent();
3221                } else {
3222                    self.write_space();
3223                }
3224                self.write_keyword("SETTINGS");
3225                self.write_space();
3226                for (i, expr) in settings.iter().enumerate() {
3227                    if i > 0 {
3228                        self.write(", ");
3229                    }
3230                    self.generate_expression(expr)?;
3231                }
3232            }
3233
3234            if let Some(format_expr) = &select.format {
3235                if self.config.pretty {
3236                    self.write_newline();
3237                    self.write_indent();
3238                } else {
3239                    self.write_space();
3240                }
3241                self.write_keyword("FORMAT");
3242                self.write_space();
3243                self.generate_expression(format_expr)?;
3244            }
3245        }
3246
3247        // FETCH FIRST/NEXT
3248        if let Some(fetch) = &select.fetch {
3249            // Check if we already emitted LIMIT from FETCH before OFFSET
3250            let fetch_already_as_limit = select.offset.is_some() && !fetch.percent && !fetch.with_ties && fetch.count.is_some() && matches!(
3251                self.config.dialect,
3252                Some(DialectType::Spark) | Some(DialectType::Hive)
3253                | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
3254                | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
3255                | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
3256                | Some(DialectType::Redshift)
3257            );
3258
3259            if fetch_already_as_limit {
3260                // Already emitted as LIMIT before OFFSET, skip
3261            } else {
3262            if self.config.pretty {
3263                self.write_newline();
3264                self.write_indent();
3265            } else {
3266                self.write_space();
3267            }
3268
3269            // Convert FETCH to LIMIT for dialects that prefer LIMIT syntax
3270            let use_limit = !fetch.percent && !fetch.with_ties && fetch.count.is_some() && matches!(
3271                self.config.dialect,
3272                Some(DialectType::Spark) | Some(DialectType::Hive)
3273                | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
3274                | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
3275                | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
3276                | Some(DialectType::Redshift)
3277            );
3278
3279            if use_limit {
3280                self.write_keyword("LIMIT");
3281                self.write_space();
3282                self.generate_expression(fetch.count.as_ref().unwrap())?;
3283            } else {
3284                self.write_keyword("FETCH");
3285                self.write_space();
3286                self.write_keyword(&fetch.direction);
3287                if let Some(ref count) = fetch.count {
3288                    self.write_space();
3289                    self.generate_expression(count)?;
3290                }
3291                if fetch.percent {
3292                    self.write_space();
3293                    self.write_keyword("PERCENT");
3294                }
3295                if fetch.rows {
3296                    self.write_space();
3297                    self.write_keyword("ROWS");
3298                }
3299                if fetch.with_ties {
3300                    self.write_space();
3301                    self.write_keyword("WITH TIES");
3302                } else {
3303                    self.write_space();
3304                    self.write_keyword("ONLY");
3305                }
3306            }
3307            } // close fetch_already_as_limit else
3308        }
3309
3310        // SAMPLE / TABLESAMPLE
3311        if let Some(sample) = &select.sample {
3312            use crate::dialects::DialectType;
3313            if self.config.pretty {
3314                self.write_newline();
3315            } else {
3316                self.write_space();
3317            }
3318
3319            if sample.is_using_sample {
3320                // DuckDB USING SAMPLE: METHOD (size UNIT) [REPEATABLE (seed)]
3321                self.write_keyword("USING SAMPLE");
3322                self.generate_sample_body(sample)?;
3323            } else {
3324                self.write_keyword("TABLESAMPLE");
3325
3326                // Snowflake defaults to BERNOULLI when no explicit method is given
3327                let snowflake_bernoulli = matches!(self.config.dialect, Some(DialectType::Snowflake)) && !sample.explicit_method;
3328                if snowflake_bernoulli {
3329                    self.write_space();
3330                    self.write_keyword("BERNOULLI");
3331                }
3332
3333                // Handle BUCKET sampling: TABLESAMPLE (BUCKET 1 OUT OF 5 ON x)
3334                if matches!(sample.method, SampleMethod::Bucket) {
3335                    self.write_space();
3336                    self.write("(");
3337                    self.write_keyword("BUCKET");
3338                    self.write_space();
3339                    if let Some(ref num) = sample.bucket_numerator {
3340                        self.generate_expression(num)?;
3341                    }
3342                    self.write_space();
3343                    self.write_keyword("OUT OF");
3344                    self.write_space();
3345                    if let Some(ref denom) = sample.bucket_denominator {
3346                        self.generate_expression(denom)?;
3347                    }
3348                    if let Some(ref field) = sample.bucket_field {
3349                        self.write_space();
3350                        self.write_keyword("ON");
3351                        self.write_space();
3352                        self.generate_expression(field)?;
3353                    }
3354                    self.write(")");
3355                } else if sample.unit_after_size {
3356                    // Syntax: TABLESAMPLE [METHOD] (size ROWS) or TABLESAMPLE [METHOD] (size PERCENT)
3357                    if sample.explicit_method && sample.method_before_size {
3358                        self.write_space();
3359                        match sample.method {
3360                            SampleMethod::Bernoulli => self.write_keyword("BERNOULLI"),
3361                            SampleMethod::System => self.write_keyword("SYSTEM"),
3362                            SampleMethod::Block => self.write_keyword("BLOCK"),
3363                            SampleMethod::Row => self.write_keyword("ROW"),
3364                            SampleMethod::Reservoir => self.write_keyword("RESERVOIR"),
3365                            _ => {}
3366                        }
3367                    }
3368                    self.write(" (");
3369                    self.generate_expression(&sample.size)?;
3370                    self.write_space();
3371                    match sample.method {
3372                        SampleMethod::Percent => self.write_keyword("PERCENT"),
3373                        SampleMethod::Row => self.write_keyword("ROWS"),
3374                        SampleMethod::Reservoir => self.write_keyword("ROWS"),
3375                        _ => {
3376                            self.write_keyword("PERCENT");
3377                        }
3378                    }
3379                    self.write(")");
3380                } else {
3381                    // Syntax: TABLESAMPLE METHOD (size)
3382                    self.write_space();
3383                    match sample.method {
3384                        SampleMethod::Bernoulli => self.write_keyword("BERNOULLI"),
3385                        SampleMethod::System => self.write_keyword("SYSTEM"),
3386                        SampleMethod::Block => self.write_keyword("BLOCK"),
3387                        SampleMethod::Row => self.write_keyword("ROW"),
3388                        SampleMethod::Percent => self.write_keyword("BERNOULLI"),
3389                        SampleMethod::Bucket => {}
3390                        SampleMethod::Reservoir => self.write_keyword("RESERVOIR"),
3391                    }
3392                    self.write(" (");
3393                    self.generate_expression(&sample.size)?;
3394                    if matches!(sample.method, SampleMethod::Percent) {
3395                        self.write_space();
3396                        self.write_keyword("PERCENT");
3397                    }
3398                    self.write(")");
3399                }
3400            }
3401
3402            if let Some(seed) = &sample.seed {
3403                self.write_space();
3404                // Databricks/Spark use REPEATABLE, not SEED
3405                let use_seed = sample.use_seed_keyword
3406                    && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
3407                if use_seed {
3408                    self.write_keyword("SEED");
3409                } else {
3410                    self.write_keyword("REPEATABLE");
3411                }
3412                self.write(" (");
3413                self.generate_expression(seed)?;
3414                self.write(")");
3415            }
3416        }
3417
3418        // FOR UPDATE/SHARE locks
3419        // Skip locking clauses for dialects that don't support them
3420        if self.config.locking_reads_supported {
3421            for lock in &select.locks {
3422                if self.config.pretty {
3423                    self.write_newline();
3424                    self.write_indent();
3425                } else {
3426                    self.write_space();
3427                }
3428                self.generate_lock(lock)?;
3429            }
3430        }
3431
3432        // FOR XML clause (T-SQL)
3433        if !select.for_xml.is_empty() {
3434            if self.config.pretty {
3435                self.write_newline();
3436                self.write_indent();
3437            } else {
3438                self.write_space();
3439            }
3440            self.write_keyword("FOR XML");
3441            for (i, opt) in select.for_xml.iter().enumerate() {
3442                if self.config.pretty {
3443                    if i > 0 {
3444                        self.write(",");
3445                    }
3446                    self.write_newline();
3447                    self.write_indent();
3448                    self.write("  "); // extra indent for options
3449                } else {
3450                    if i > 0 {
3451                        self.write(",");
3452                    }
3453                    self.write_space();
3454                }
3455                self.generate_for_xml_option(opt)?;
3456            }
3457        }
3458
3459        // TSQL: OPTION clause
3460        if let Some(ref option) = select.option {
3461            if matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)) {
3462                self.write_space();
3463                self.write(option);
3464            }
3465        }
3466
3467        Ok(())
3468    }
3469
3470    /// Generate a single FOR XML option
3471    fn generate_for_xml_option(&mut self, opt: &Expression) -> Result<()> {
3472        match opt {
3473            Expression::QueryOption(qo) => {
3474                // Extract the option name from Var
3475                if let Expression::Var(var) = &*qo.this {
3476                    self.write(&var.this);
3477                } else {
3478                    self.generate_expression(&qo.this)?;
3479                }
3480                // If there's an expression (like PATH('element')), output it in parens
3481                if let Some(expr) = &qo.expression {
3482                    self.write("(");
3483                    self.generate_expression(expr)?;
3484                    self.write(")");
3485                }
3486            }
3487            _ => {
3488                self.generate_expression(opt)?;
3489            }
3490        }
3491        Ok(())
3492    }
3493
3494    fn generate_with(&mut self, with: &With) -> Result<()> {
3495        use crate::dialects::DialectType;
3496
3497        // Output leading comments before WITH
3498        for comment in &with.leading_comments {
3499            self.write(comment);
3500            self.write(" ");
3501        }
3502        self.write_keyword("WITH");
3503        if with.recursive {
3504            self.write_space();
3505            self.write_keyword("RECURSIVE");
3506        }
3507        self.write_space();
3508
3509        // BigQuery doesn't support column aliases in CTE definitions
3510        let skip_cte_columns = matches!(self.config.dialect, Some(DialectType::BigQuery));
3511
3512        for (i, cte) in with.ctes.iter().enumerate() {
3513            if i > 0 {
3514                self.write(",");
3515                if self.config.pretty {
3516                    self.write_space();
3517                } else {
3518                    self.write(" ");
3519                }
3520            }
3521            if matches!(self.config.dialect, Some(DialectType::ClickHouse)) && !cte.alias_first {
3522                self.generate_expression(&cte.this)?;
3523                self.write_space();
3524                self.write_keyword("AS");
3525                self.write_space();
3526                self.generate_identifier(&cte.alias)?;
3527                continue;
3528            }
3529            self.generate_identifier(&cte.alias)?;
3530            if !cte.columns.is_empty() && !skip_cte_columns {
3531                self.write("(");
3532                for (j, col) in cte.columns.iter().enumerate() {
3533                    if j > 0 {
3534                        self.write(", ");
3535                    }
3536                    self.generate_identifier(col)?;
3537                }
3538                self.write(")");
3539            }
3540            // USING KEY (columns) for DuckDB recursive CTEs
3541            if !cte.key_expressions.is_empty() {
3542                self.write_space();
3543                self.write_keyword("USING KEY");
3544                self.write(" (");
3545                for (i, key) in cte.key_expressions.iter().enumerate() {
3546                    if i > 0 {
3547                        self.write(", ");
3548                    }
3549                    self.generate_identifier(key)?;
3550                }
3551                self.write(")");
3552            }
3553            self.write_space();
3554            self.write_keyword("AS");
3555            // MATERIALIZED / NOT MATERIALIZED
3556            if let Some(materialized) = cte.materialized {
3557                self.write_space();
3558                if materialized {
3559                    self.write_keyword("MATERIALIZED");
3560                } else {
3561                    self.write_keyword("NOT MATERIALIZED");
3562                }
3563            }
3564            self.write(" (");
3565            if self.config.pretty {
3566                self.write_newline();
3567                self.indent_level += 1;
3568                self.write_indent();
3569            }
3570            // For Spark/Databricks, VALUES in a CTE must be wrapped with SELECT * FROM
3571            // e.g., WITH t AS (VALUES ('foo_val') AS t(foo1)) -> WITH t AS (SELECT * FROM VALUES ('foo_val') AS t(foo1))
3572            let wrap_values_in_select = matches!(
3573                self.config.dialect,
3574                Some(DialectType::Spark) | Some(DialectType::Databricks)
3575            ) && matches!(&cte.this, Expression::Values(_));
3576
3577            if wrap_values_in_select {
3578                self.write_keyword("SELECT");
3579                self.write(" * ");
3580                self.write_keyword("FROM");
3581                self.write_space();
3582            }
3583            self.generate_expression(&cte.this)?;
3584            if self.config.pretty {
3585                self.write_newline();
3586                self.indent_level -= 1;
3587                self.write_indent();
3588            }
3589            self.write(")");
3590        }
3591
3592        // Generate SEARCH/CYCLE clause if present
3593        if let Some(search) = &with.search {
3594            self.write_space();
3595            self.generate_expression(search)?;
3596        }
3597
3598        Ok(())
3599    }
3600
3601    fn generate_join(&mut self, join: &Join) -> Result<()> {
3602        // Implicit (comma) joins: output as ", table" instead of "CROSS JOIN table"
3603        if join.kind == JoinKind::Implicit {
3604            self.write(",");
3605            if self.config.pretty {
3606                self.write_newline();
3607                self.write_indent();
3608            } else {
3609                self.write_space();
3610            }
3611            self.generate_expression(&join.this)?;
3612            return Ok(());
3613        }
3614
3615        if self.config.pretty {
3616            self.write_newline();
3617            self.write_indent();
3618        } else {
3619            self.write_space();
3620        }
3621
3622        // Helper: format hint suffix (e.g., " LOOP" or "")
3623        // Only include join hints for dialects that support them
3624        let hint_str = if self.config.join_hints {
3625            join.join_hint.as_ref().map(|h| format!(" {}", h)).unwrap_or_default()
3626        } else {
3627            String::new()
3628        };
3629
3630        let clickhouse_join_keyword = if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
3631            if let Some(hint) = &join.join_hint {
3632                let mut global = false;
3633                let mut strictness: Option<&'static str> = None;
3634                for part in hint.split_whitespace() {
3635                    match part.to_uppercase().as_str() {
3636                        "GLOBAL" => global = true,
3637                        "ANY" => strictness = Some("ANY"),
3638                        "ASOF" => strictness = Some("ASOF"),
3639                        "SEMI" => strictness = Some("SEMI"),
3640                        "ANTI" => strictness = Some("ANTI"),
3641                        _ => {}
3642                    }
3643                }
3644
3645                if global || strictness.is_some() {
3646                    let join_type = match join.kind {
3647                        JoinKind::Left => {
3648                            if join.use_outer_keyword {
3649                                "LEFT OUTER"
3650                            } else if join.use_inner_keyword {
3651                                "LEFT INNER"
3652                            } else {
3653                                "LEFT"
3654                            }
3655                        }
3656                        JoinKind::Right => {
3657                            if join.use_outer_keyword {
3658                                "RIGHT OUTER"
3659                            } else if join.use_inner_keyword {
3660                                "RIGHT INNER"
3661                            } else {
3662                                "RIGHT"
3663                            }
3664                        }
3665                        JoinKind::Full => {
3666                            if join.use_outer_keyword {
3667                                "FULL OUTER"
3668                            } else {
3669                                "FULL"
3670                            }
3671                        }
3672                        JoinKind::Inner => {
3673                            if join.use_inner_keyword {
3674                                "INNER"
3675                            } else {
3676                                ""
3677                            }
3678                        }
3679                        _ => "",
3680                    };
3681
3682                    let mut parts = Vec::new();
3683                    if global {
3684                        parts.push("GLOBAL");
3685                    }
3686                    if !join_type.is_empty() {
3687                        parts.push(join_type);
3688                    }
3689                    if let Some(strict) = strictness {
3690                        parts.push(strict);
3691                    }
3692                    parts.push("JOIN");
3693                    Some(parts.join(" "))
3694                } else {
3695                    None
3696                }
3697            } else {
3698                None
3699            }
3700        } else {
3701            None
3702        };
3703
3704        if let Some(keyword) = clickhouse_join_keyword {
3705            self.write_keyword(&keyword);
3706        } else {
3707            match join.kind {
3708            JoinKind::Inner => {
3709                if join.use_inner_keyword {
3710                    self.write_keyword(&format!("INNER{} JOIN", hint_str));
3711                } else {
3712                    self.write_keyword(&format!("{}JOIN", if hint_str.is_empty() { String::new() } else { format!("{} ", hint_str.trim()) }));
3713                }
3714            }
3715            JoinKind::Left => {
3716                if join.use_outer_keyword {
3717                    self.write_keyword(&format!("LEFT OUTER{} JOIN", hint_str));
3718                } else if join.use_inner_keyword {
3719                    self.write_keyword(&format!("LEFT INNER{} JOIN", hint_str));
3720                } else {
3721                    self.write_keyword(&format!("LEFT{} JOIN", hint_str));
3722                }
3723            }
3724            JoinKind::Right => {
3725                if join.use_outer_keyword {
3726                    self.write_keyword(&format!("RIGHT OUTER{} JOIN", hint_str));
3727                } else if join.use_inner_keyword {
3728                    self.write_keyword(&format!("RIGHT INNER{} JOIN", hint_str));
3729                } else {
3730                    self.write_keyword(&format!("RIGHT{} JOIN", hint_str));
3731                }
3732            }
3733            JoinKind::Full => {
3734                if join.use_outer_keyword {
3735                    self.write_keyword(&format!("FULL OUTER{} JOIN", hint_str));
3736                } else {
3737                    self.write_keyword(&format!("FULL{} JOIN", hint_str));
3738                }
3739            }
3740            JoinKind::Outer => self.write_keyword("OUTER JOIN"),
3741            JoinKind::Cross => self.write_keyword("CROSS JOIN"),
3742            JoinKind::Natural => self.write_keyword("NATURAL JOIN"),
3743            JoinKind::NaturalLeft => {
3744                if join.use_outer_keyword {
3745                    self.write_keyword("NATURAL LEFT OUTER JOIN");
3746                } else {
3747                    self.write_keyword("NATURAL LEFT JOIN");
3748                }
3749            }
3750            JoinKind::NaturalRight => {
3751                if join.use_outer_keyword {
3752                    self.write_keyword("NATURAL RIGHT OUTER JOIN");
3753                } else {
3754                    self.write_keyword("NATURAL RIGHT JOIN");
3755                }
3756            }
3757            JoinKind::NaturalFull => {
3758                if join.use_outer_keyword {
3759                    self.write_keyword("NATURAL FULL OUTER JOIN");
3760                } else {
3761                    self.write_keyword("NATURAL FULL JOIN");
3762                }
3763            }
3764            JoinKind::Semi => self.write_keyword("SEMI JOIN"),
3765            JoinKind::Anti => self.write_keyword("ANTI JOIN"),
3766            JoinKind::LeftSemi => self.write_keyword("LEFT SEMI JOIN"),
3767            JoinKind::LeftAnti => self.write_keyword("LEFT ANTI JOIN"),
3768            JoinKind::RightSemi => self.write_keyword("RIGHT SEMI JOIN"),
3769            JoinKind::RightAnti => self.write_keyword("RIGHT ANTI JOIN"),
3770            JoinKind::CrossApply => {
3771                // CROSS APPLY -> INNER JOIN LATERAL for non-TSQL dialects
3772                if matches!(self.config.dialect, Some(DialectType::TSQL) | None) {
3773                    self.write_keyword("CROSS APPLY");
3774                } else {
3775                    self.write_keyword("INNER JOIN LATERAL");
3776                }
3777            }
3778            JoinKind::OuterApply => {
3779                // OUTER APPLY -> LEFT JOIN LATERAL for non-TSQL dialects
3780                if matches!(self.config.dialect, Some(DialectType::TSQL) | None) {
3781                    self.write_keyword("OUTER APPLY");
3782                } else {
3783                    self.write_keyword("LEFT JOIN LATERAL");
3784                }
3785            }
3786            JoinKind::AsOf => self.write_keyword("ASOF JOIN"),
3787            JoinKind::AsOfLeft => {
3788                if join.use_outer_keyword {
3789                    self.write_keyword("ASOF LEFT OUTER JOIN");
3790                } else {
3791                    self.write_keyword("ASOF LEFT JOIN");
3792                }
3793            }
3794            JoinKind::AsOfRight => {
3795                if join.use_outer_keyword {
3796                    self.write_keyword("ASOF RIGHT OUTER JOIN");
3797                } else {
3798                    self.write_keyword("ASOF RIGHT JOIN");
3799                }
3800            }
3801            JoinKind::Lateral => self.write_keyword("LATERAL JOIN"),
3802            JoinKind::LeftLateral => {
3803                if join.use_outer_keyword {
3804                    self.write_keyword("LEFT OUTER LATERAL JOIN");
3805                } else {
3806                    self.write_keyword("LEFT LATERAL JOIN");
3807                }
3808            }
3809            JoinKind::Straight => self.write_keyword("STRAIGHT_JOIN"),
3810            JoinKind::Implicit => {
3811                // BigQuery, Hive, Spark, and Databricks prefer explicit CROSS JOIN over comma syntax
3812                use crate::dialects::DialectType;
3813                if matches!(self.config.dialect, Some(DialectType::BigQuery) | Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)) {
3814                    self.write_keyword("CROSS JOIN");
3815                } else {
3816                    // Implicit join uses comma: FROM a, b
3817                    // We already wrote a space before the match, so replace with comma
3818                    // by removing trailing space and writing ", "
3819                    self.output.truncate(self.output.trim_end().len());
3820                    self.write(",");
3821                }
3822            }
3823            JoinKind::Array => self.write_keyword("ARRAY JOIN"),
3824            JoinKind::LeftArray => self.write_keyword("LEFT ARRAY JOIN"),
3825        }
3826        }
3827
3828        // ARRAY JOIN items need comma-separated output (Tuple holds multiple items)
3829        if matches!(join.kind, JoinKind::Array | JoinKind::LeftArray) {
3830            self.write_space();
3831            match &join.this {
3832                Expression::Tuple(t) => {
3833                    for (i, item) in t.expressions.iter().enumerate() {
3834                        if i > 0 {
3835                            self.write(", ");
3836                        }
3837                        self.generate_expression(item)?;
3838                    }
3839                }
3840                other => {
3841                    self.generate_expression(other)?;
3842                }
3843            }
3844        } else {
3845        self.write_space();
3846        self.generate_expression(&join.this)?;
3847        }
3848
3849        // Only output MATCH_CONDITION/ON/USING inline if the condition wasn't deferred
3850        if !join.deferred_condition {
3851            // Output MATCH_CONDITION first (Snowflake ASOF JOIN)
3852            if let Some(match_cond) = &join.match_condition {
3853                self.write_space();
3854                self.write_keyword("MATCH_CONDITION");
3855                self.write(" (");
3856                self.generate_expression(match_cond)?;
3857                self.write(")");
3858            }
3859
3860            if let Some(on) = &join.on {
3861                if self.config.pretty {
3862                    self.write_newline();
3863                    self.indent_level += 1;
3864                    self.write_indent();
3865                    self.write_keyword("ON");
3866                    self.write_space();
3867                    self.generate_join_on_condition(on)?;
3868                    self.indent_level -= 1;
3869                } else {
3870                    self.write_space();
3871                    self.write_keyword("ON");
3872                    self.write_space();
3873                    self.generate_expression(on)?;
3874                }
3875            }
3876
3877            if !join.using.is_empty() {
3878                if self.config.pretty {
3879                    self.write_newline();
3880                    self.indent_level += 1;
3881                    self.write_indent();
3882                    self.write_keyword("USING");
3883                    self.write(" (");
3884                    for (i, col) in join.using.iter().enumerate() {
3885                        if i > 0 {
3886                            self.write(", ");
3887                        }
3888                        self.generate_identifier(col)?;
3889                    }
3890                    self.write(")");
3891                    self.indent_level -= 1;
3892                } else {
3893                    self.write_space();
3894                    self.write_keyword("USING");
3895                    self.write(" (");
3896                    for (i, col) in join.using.iter().enumerate() {
3897                        if i > 0 {
3898                            self.write(", ");
3899                        }
3900                        self.generate_identifier(col)?;
3901                    }
3902                    self.write(")");
3903                }
3904            }
3905        }
3906
3907        // Generate PIVOT/UNPIVOT expressions that follow this join
3908        for pivot in &join.pivots {
3909            self.write_space();
3910            self.generate_expression(pivot)?;
3911        }
3912
3913        Ok(())
3914    }
3915
3916    /// Generate just the ON/USING/MATCH_CONDITION for a join (used for deferred conditions)
3917    fn generate_join_condition(&mut self, join: &Join) -> Result<()> {
3918        // Generate MATCH_CONDITION first (Snowflake ASOF JOIN)
3919        if let Some(match_cond) = &join.match_condition {
3920            self.write_space();
3921            self.write_keyword("MATCH_CONDITION");
3922            self.write(" (");
3923            self.generate_expression(match_cond)?;
3924            self.write(")");
3925        }
3926
3927        if let Some(on) = &join.on {
3928            if self.config.pretty {
3929                self.write_newline();
3930                self.indent_level += 1;
3931                self.write_indent();
3932                self.write_keyword("ON");
3933                self.write_space();
3934                // In pretty mode, split AND conditions onto separate lines
3935                self.generate_join_on_condition(on)?;
3936                self.indent_level -= 1;
3937            } else {
3938                self.write_space();
3939                self.write_keyword("ON");
3940                self.write_space();
3941                self.generate_expression(on)?;
3942            }
3943        }
3944
3945        if !join.using.is_empty() {
3946            if self.config.pretty {
3947                self.write_newline();
3948                self.indent_level += 1;
3949                self.write_indent();
3950                self.write_keyword("USING");
3951                self.write(" (");
3952                for (i, col) in join.using.iter().enumerate() {
3953                    if i > 0 {
3954                        self.write(", ");
3955                    }
3956                    self.generate_identifier(col)?;
3957                }
3958                self.write(")");
3959                self.indent_level -= 1;
3960            } else {
3961                self.write_space();
3962                self.write_keyword("USING");
3963                self.write(" (");
3964                for (i, col) in join.using.iter().enumerate() {
3965                    if i > 0 {
3966                        self.write(", ");
3967                    }
3968                    self.generate_identifier(col)?;
3969                }
3970                self.write(")");
3971            }
3972        }
3973
3974        // Generate PIVOT/UNPIVOT expressions that follow this join (for deferred conditions)
3975        for pivot in &join.pivots {
3976            self.write_space();
3977            self.generate_expression(pivot)?;
3978        }
3979
3980        Ok(())
3981    }
3982
3983    /// Generate JOIN ON condition with AND clauses on separate lines in pretty mode
3984    fn generate_join_on_condition(&mut self, expr: &Expression) -> Result<()> {
3985        // If it's an AND expression, split each condition onto a new line
3986        if let Expression::And(and_op) = expr {
3987            // Generate left side (might be another AND)
3988            self.generate_join_on_condition(&and_op.left)?;
3989            // AND on new line
3990            self.write_newline();
3991            self.write_indent();
3992            self.write_keyword("AND");
3993            self.write_space();
3994            // Generate right side (should be a single condition)
3995            self.generate_expression(&and_op.right)?;
3996        } else {
3997            // Base case: single condition
3998            self.generate_expression(expr)?;
3999        }
4000        Ok(())
4001    }
4002
4003    fn generate_joined_table(&mut self, jt: &JoinedTable) -> Result<()> {
4004        // Parenthesized join: (tbl1 CROSS JOIN tbl2)
4005        self.write("(");
4006        self.generate_expression(&jt.left)?;
4007
4008        // Generate all joins
4009        for join in &jt.joins {
4010            self.generate_join(join)?;
4011        }
4012
4013        // Generate LATERAL VIEW clauses (Hive/Spark)
4014        for lv in &jt.lateral_views {
4015            self.generate_lateral_view(lv)?;
4016        }
4017
4018        self.write(")");
4019
4020        // Alias
4021        if let Some(alias) = &jt.alias {
4022            self.write_space();
4023            self.write_keyword("AS");
4024            self.write_space();
4025            self.generate_identifier(alias)?;
4026        }
4027
4028        Ok(())
4029    }
4030
4031    fn generate_lateral_view(&mut self, lv: &LateralView) -> Result<()> {
4032        use crate::dialects::DialectType;
4033
4034        if self.config.pretty {
4035            self.write_newline();
4036            self.write_indent();
4037        } else {
4038            self.write_space();
4039        }
4040
4041        // For Hive/Spark/Databricks (or no dialect specified), output native LATERAL VIEW syntax
4042        // For PostgreSQL and other specific dialects, convert to CROSS JOIN (LATERAL or UNNEST)
4043        let use_lateral_join = matches!(
4044            self.config.dialect,
4045            Some(DialectType::PostgreSQL) | Some(DialectType::DuckDB) |
4046            Some(DialectType::Snowflake) | Some(DialectType::TSQL) |
4047            Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena)
4048        );
4049
4050        // Check if target dialect should use UNNEST instead of EXPLODE
4051        let use_unnest = matches!(
4052            self.config.dialect,
4053            Some(DialectType::DuckDB) | Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena)
4054        );
4055
4056        // Check if we need POSEXPLODE -> UNNEST WITH ORDINALITY
4057        let (is_posexplode, func_args) = match &lv.this {
4058            Expression::Explode(uf) => {
4059                // Expression::Explode is the dedicated EXPLODE expression type
4060                (false, vec![uf.this.clone()])
4061            }
4062            Expression::Function(func) => {
4063                let name = func.name.to_uppercase();
4064                if name == "POSEXPLODE" || name == "POSEXPLODE_OUTER" {
4065                    (true, func.args.clone())
4066                } else if name == "EXPLODE" || name == "EXPLODE_OUTER" || name == "INLINE" {
4067                    (false, func.args.clone())
4068                } else {
4069                    (false, vec![])
4070                }
4071            }
4072            _ => (false, vec![]),
4073        };
4074
4075        if use_lateral_join {
4076            // Convert to CROSS JOIN for PostgreSQL-like dialects
4077            if lv.outer {
4078                self.write_keyword("LEFT JOIN LATERAL");
4079            } else {
4080                self.write_keyword("CROSS JOIN");
4081            }
4082            self.write_space();
4083
4084            if use_unnest && !func_args.is_empty() {
4085                // Convert EXPLODE(y) -> UNNEST(y), POSEXPLODE(y) -> UNNEST(y)
4086                // For DuckDB, also convert ARRAY(y) -> [y]
4087                let unnest_args = if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
4088                    // DuckDB: ARRAY(y) -> [y]
4089                    func_args.iter().map(|a| {
4090                        if let Expression::Function(ref f) = a {
4091                            if f.name.to_uppercase() == "ARRAY" && f.args.len() == 1 {
4092                                return Expression::ArrayFunc(Box::new(crate::expressions::ArrayConstructor {
4093                                    expressions: f.args.clone(),
4094                                    bracket_notation: true,
4095                                    use_list_keyword: false,
4096                                }));
4097                            }
4098                        }
4099                        a.clone()
4100                    }).collect::<Vec<_>>()
4101                } else if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena)) {
4102                    // Presto: ARRAY(y) -> ARRAY[y]
4103                    func_args.iter().map(|a| {
4104                        if let Expression::Function(ref f) = a {
4105                            if f.name.to_uppercase() == "ARRAY" && f.args.len() >= 1 {
4106                                return Expression::ArrayFunc(Box::new(crate::expressions::ArrayConstructor {
4107                                    expressions: f.args.clone(),
4108                                    bracket_notation: true,
4109                                    use_list_keyword: false,
4110                                }));
4111                            }
4112                        }
4113                        a.clone()
4114                    }).collect::<Vec<_>>()
4115                } else {
4116                    func_args
4117                };
4118
4119                self.write_keyword("UNNEST");
4120                self.write("(");
4121                for (i, arg) in unnest_args.iter().enumerate() {
4122                    if i > 0 { self.write(", "); }
4123                    self.generate_expression(arg)?;
4124                }
4125                self.write(")");
4126
4127                // POSEXPLODE -> WITH ORDINALITY
4128                if is_posexplode {
4129                    self.write_space();
4130                    self.write_keyword("WITH ORDINALITY");
4131                }
4132            } else {
4133                // Not EXPLODE/POSEXPLODE or not using UNNEST, use LATERAL
4134                if !lv.outer {
4135                    self.write_keyword("LATERAL");
4136                    self.write_space();
4137                }
4138                self.generate_expression(&lv.this)?;
4139            }
4140
4141            // Add table and column aliases
4142            if let Some(alias) = &lv.table_alias {
4143                self.write_space();
4144                self.write_keyword("AS");
4145                self.write_space();
4146                self.generate_identifier(alias)?;
4147                if !lv.column_aliases.is_empty() {
4148                    self.write("(");
4149                    for (i, col) in lv.column_aliases.iter().enumerate() {
4150                        if i > 0 {
4151                            self.write(", ");
4152                        }
4153                        self.generate_identifier(col)?;
4154                    }
4155                    self.write(")");
4156                }
4157            } else if !lv.column_aliases.is_empty() {
4158                // Column aliases without table alias
4159                self.write_space();
4160                self.write_keyword("AS");
4161                self.write(" t(");
4162                for (i, col) in lv.column_aliases.iter().enumerate() {
4163                    if i > 0 {
4164                        self.write(", ");
4165                    }
4166                    self.generate_identifier(col)?;
4167                }
4168                self.write(")");
4169            }
4170
4171            // For LEFT JOIN LATERAL, need ON TRUE
4172            if lv.outer {
4173                self.write_space();
4174                self.write_keyword("ON TRUE");
4175            }
4176        } else {
4177            // Output native LATERAL VIEW syntax (Hive/Spark/Databricks or default)
4178            self.write_keyword("LATERAL VIEW");
4179            if lv.outer {
4180                self.write_space();
4181                self.write_keyword("OUTER");
4182            }
4183            self.write_space();
4184            self.generate_expression(&lv.this)?;
4185
4186            // Table alias
4187            if let Some(alias) = &lv.table_alias {
4188                self.write_space();
4189                self.generate_identifier(alias)?;
4190            }
4191
4192            // Column aliases
4193            if !lv.column_aliases.is_empty() {
4194                self.write_space();
4195                self.write_keyword("AS");
4196                self.write_space();
4197                for (i, col) in lv.column_aliases.iter().enumerate() {
4198                    if i > 0 {
4199                        self.write(", ");
4200                    }
4201                    self.generate_identifier(col)?;
4202                }
4203            }
4204        }
4205
4206        Ok(())
4207    }
4208
4209    fn generate_union(&mut self, union: &Union) -> Result<()> {
4210        // WITH clause
4211        if let Some(with) = &union.with {
4212            self.generate_with(with)?;
4213            self.write_space();
4214        }
4215        self.generate_expression(&union.left)?;
4216        if self.config.pretty {
4217            self.write_newline();
4218            self.write_indent();
4219        } else {
4220            self.write_space();
4221        }
4222
4223        // BigQuery set operation modifiers: [side] [kind] UNION
4224        if let Some(side) = &union.side {
4225            self.write_keyword(side);
4226            self.write_space();
4227        }
4228        if let Some(kind) = &union.kind {
4229            self.write_keyword(kind);
4230            self.write_space();
4231        }
4232
4233        self.write_keyword("UNION");
4234        if union.all {
4235            self.write_space();
4236            self.write_keyword("ALL");
4237        } else if union.distinct {
4238            self.write_space();
4239            self.write_keyword("DISTINCT");
4240        }
4241
4242        // BigQuery: CORRESPONDING/STRICT CORRESPONDING -> BY NAME, BY (cols) -> ON (cols)
4243        // DuckDB: BY NAME
4244        if union.corresponding || union.by_name {
4245            self.write_space();
4246            self.write_keyword("BY NAME");
4247        }
4248        if !union.on_columns.is_empty() {
4249            self.write_space();
4250            self.write_keyword("ON");
4251            self.write(" (");
4252            for (i, col) in union.on_columns.iter().enumerate() {
4253                if i > 0 {
4254                    self.write(", ");
4255                }
4256                self.generate_expression(col)?;
4257            }
4258            self.write(")");
4259        }
4260
4261        if self.config.pretty {
4262            self.write_newline();
4263            self.write_indent();
4264        } else {
4265            self.write_space();
4266        }
4267        self.generate_expression(&union.right)?;
4268        // ORDER BY, LIMIT, OFFSET for the set operation
4269        if let Some(order_by) = &union.order_by {
4270            if self.config.pretty {
4271                self.write_newline();
4272            } else {
4273                self.write_space();
4274            }
4275            self.write_keyword("ORDER BY");
4276            self.write_space();
4277            for (i, ordered) in order_by.expressions.iter().enumerate() {
4278                if i > 0 {
4279                    self.write(", ");
4280                }
4281                self.generate_ordered(ordered)?;
4282            }
4283        }
4284        if let Some(limit) = &union.limit {
4285            if self.config.pretty {
4286                self.write_newline();
4287            } else {
4288                self.write_space();
4289            }
4290            self.write_keyword("LIMIT");
4291            self.write_space();
4292            self.generate_expression(limit)?;
4293        }
4294        if let Some(offset) = &union.offset {
4295            if self.config.pretty {
4296                self.write_newline();
4297            } else {
4298                self.write_space();
4299            }
4300            self.write_keyword("OFFSET");
4301            self.write_space();
4302            self.generate_expression(offset)?;
4303        }
4304        // DISTRIBUTE BY (Hive/Spark)
4305        if let Some(distribute_by) = &union.distribute_by {
4306            self.write_space();
4307            self.write_keyword("DISTRIBUTE BY");
4308            self.write_space();
4309            for (i, expr) in distribute_by.expressions.iter().enumerate() {
4310                if i > 0 {
4311                    self.write(", ");
4312                }
4313                self.generate_expression(expr)?;
4314            }
4315        }
4316        // SORT BY (Hive/Spark)
4317        if let Some(sort_by) = &union.sort_by {
4318            self.write_space();
4319            self.write_keyword("SORT BY");
4320            self.write_space();
4321            for (i, ord) in sort_by.expressions.iter().enumerate() {
4322                if i > 0 {
4323                    self.write(", ");
4324                }
4325                self.generate_ordered(ord)?;
4326            }
4327        }
4328        // CLUSTER BY (Hive/Spark)
4329        if let Some(cluster_by) = &union.cluster_by {
4330            self.write_space();
4331            self.write_keyword("CLUSTER BY");
4332            self.write_space();
4333            for (i, ord) in cluster_by.expressions.iter().enumerate() {
4334                if i > 0 {
4335                    self.write(", ");
4336                }
4337                self.generate_ordered(ord)?;
4338            }
4339        }
4340        Ok(())
4341    }
4342
4343    fn generate_intersect(&mut self, intersect: &Intersect) -> Result<()> {
4344        // WITH clause
4345        if let Some(with) = &intersect.with {
4346            self.generate_with(with)?;
4347            self.write_space();
4348        }
4349        self.generate_expression(&intersect.left)?;
4350        if self.config.pretty {
4351            self.write_newline();
4352            self.write_indent();
4353        } else {
4354            self.write_space();
4355        }
4356
4357        // BigQuery set operation modifiers: [side] [kind] INTERSECT
4358        if let Some(side) = &intersect.side {
4359            self.write_keyword(side);
4360            self.write_space();
4361        }
4362        if let Some(kind) = &intersect.kind {
4363            self.write_keyword(kind);
4364            self.write_space();
4365        }
4366
4367        self.write_keyword("INTERSECT");
4368        if intersect.all {
4369            self.write_space();
4370            self.write_keyword("ALL");
4371        } else if intersect.distinct {
4372            self.write_space();
4373            self.write_keyword("DISTINCT");
4374        }
4375
4376        // BigQuery: CORRESPONDING/STRICT CORRESPONDING -> BY NAME, BY (cols) -> ON (cols)
4377        // DuckDB: BY NAME
4378        if intersect.corresponding || intersect.by_name {
4379            self.write_space();
4380            self.write_keyword("BY NAME");
4381        }
4382        if !intersect.on_columns.is_empty() {
4383            self.write_space();
4384            self.write_keyword("ON");
4385            self.write(" (");
4386            for (i, col) in intersect.on_columns.iter().enumerate() {
4387                if i > 0 {
4388                    self.write(", ");
4389                }
4390                self.generate_expression(col)?;
4391            }
4392            self.write(")");
4393        }
4394
4395        if self.config.pretty {
4396            self.write_newline();
4397            self.write_indent();
4398        } else {
4399            self.write_space();
4400        }
4401        self.generate_expression(&intersect.right)?;
4402        // ORDER BY, LIMIT, OFFSET for the set operation
4403        if let Some(order_by) = &intersect.order_by {
4404            if self.config.pretty {
4405                self.write_newline();
4406            } else {
4407                self.write_space();
4408            }
4409            self.write_keyword("ORDER BY");
4410            self.write_space();
4411            for (i, ordered) in order_by.expressions.iter().enumerate() {
4412                if i > 0 {
4413                    self.write(", ");
4414                }
4415                self.generate_ordered(ordered)?;
4416            }
4417        }
4418        if let Some(limit) = &intersect.limit {
4419            if self.config.pretty {
4420                self.write_newline();
4421            } else {
4422                self.write_space();
4423            }
4424            self.write_keyword("LIMIT");
4425            self.write_space();
4426            self.generate_expression(limit)?;
4427        }
4428        if let Some(offset) = &intersect.offset {
4429            if self.config.pretty {
4430                self.write_newline();
4431            } else {
4432                self.write_space();
4433            }
4434            self.write_keyword("OFFSET");
4435            self.write_space();
4436            self.generate_expression(offset)?;
4437        }
4438        // DISTRIBUTE BY (Hive/Spark)
4439        if let Some(distribute_by) = &intersect.distribute_by {
4440            self.write_space();
4441            self.write_keyword("DISTRIBUTE BY");
4442            self.write_space();
4443            for (i, expr) in distribute_by.expressions.iter().enumerate() {
4444                if i > 0 {
4445                    self.write(", ");
4446                }
4447                self.generate_expression(expr)?;
4448            }
4449        }
4450        // SORT BY (Hive/Spark)
4451        if let Some(sort_by) = &intersect.sort_by {
4452            self.write_space();
4453            self.write_keyword("SORT BY");
4454            self.write_space();
4455            for (i, ord) in sort_by.expressions.iter().enumerate() {
4456                if i > 0 {
4457                    self.write(", ");
4458                }
4459                self.generate_ordered(ord)?;
4460            }
4461        }
4462        // CLUSTER BY (Hive/Spark)
4463        if let Some(cluster_by) = &intersect.cluster_by {
4464            self.write_space();
4465            self.write_keyword("CLUSTER BY");
4466            self.write_space();
4467            for (i, ord) in cluster_by.expressions.iter().enumerate() {
4468                if i > 0 {
4469                    self.write(", ");
4470                }
4471                self.generate_ordered(ord)?;
4472            }
4473        }
4474        Ok(())
4475    }
4476
4477    fn generate_except(&mut self, except: &Except) -> Result<()> {
4478        use crate::dialects::DialectType;
4479
4480        // WITH clause
4481        if let Some(with) = &except.with {
4482            self.generate_with(with)?;
4483            self.write_space();
4484        }
4485
4486        self.generate_expression(&except.left)?;
4487        if self.config.pretty {
4488            self.write_newline();
4489            self.write_indent();
4490        } else {
4491            self.write_space();
4492        }
4493
4494        // BigQuery set operation modifiers: [side] [kind] EXCEPT
4495        if let Some(side) = &except.side {
4496            self.write_keyword(side);
4497            self.write_space();
4498        }
4499        if let Some(kind) = &except.kind {
4500            self.write_keyword(kind);
4501            self.write_space();
4502        }
4503
4504        // Oracle uses MINUS instead of EXCEPT
4505        match self.config.dialect {
4506            Some(DialectType::Oracle) => {
4507                self.write_keyword("MINUS");
4508                // Note: Oracle MINUS doesn't support ALL
4509            }
4510            _ => {
4511                self.write_keyword("EXCEPT");
4512                if except.all {
4513                    self.write_space();
4514                    self.write_keyword("ALL");
4515                } else if except.distinct {
4516                    self.write_space();
4517                    self.write_keyword("DISTINCT");
4518                }
4519            }
4520        }
4521
4522        // BigQuery: CORRESPONDING/STRICT CORRESPONDING -> BY NAME, BY (cols) -> ON (cols)
4523        // DuckDB: BY NAME
4524        if except.corresponding || except.by_name {
4525            self.write_space();
4526            self.write_keyword("BY NAME");
4527        }
4528        if !except.on_columns.is_empty() {
4529            self.write_space();
4530            self.write_keyword("ON");
4531            self.write(" (");
4532            for (i, col) in except.on_columns.iter().enumerate() {
4533                if i > 0 {
4534                    self.write(", ");
4535                }
4536                self.generate_expression(col)?;
4537            }
4538            self.write(")");
4539        }
4540
4541        if self.config.pretty {
4542            self.write_newline();
4543            self.write_indent();
4544        } else {
4545            self.write_space();
4546        }
4547        self.generate_expression(&except.right)?;
4548        // ORDER BY, LIMIT, OFFSET for the set operation
4549        if let Some(order_by) = &except.order_by {
4550            if self.config.pretty {
4551                self.write_newline();
4552            } else {
4553                self.write_space();
4554            }
4555            self.write_keyword("ORDER BY");
4556            self.write_space();
4557            for (i, ordered) in order_by.expressions.iter().enumerate() {
4558                if i > 0 {
4559                    self.write(", ");
4560                }
4561                self.generate_ordered(ordered)?;
4562            }
4563        }
4564        if let Some(limit) = &except.limit {
4565            if self.config.pretty {
4566                self.write_newline();
4567            } else {
4568                self.write_space();
4569            }
4570            self.write_keyword("LIMIT");
4571            self.write_space();
4572            self.generate_expression(limit)?;
4573        }
4574        if let Some(offset) = &except.offset {
4575            if self.config.pretty {
4576                self.write_newline();
4577            } else {
4578                self.write_space();
4579            }
4580            self.write_keyword("OFFSET");
4581            self.write_space();
4582            self.generate_expression(offset)?;
4583        }
4584        // DISTRIBUTE BY (Hive/Spark)
4585        if let Some(distribute_by) = &except.distribute_by {
4586            self.write_space();
4587            self.write_keyword("DISTRIBUTE BY");
4588            self.write_space();
4589            for (i, expr) in distribute_by.expressions.iter().enumerate() {
4590                if i > 0 {
4591                    self.write(", ");
4592                }
4593                self.generate_expression(expr)?;
4594            }
4595        }
4596        // SORT BY (Hive/Spark)
4597        if let Some(sort_by) = &except.sort_by {
4598            self.write_space();
4599            self.write_keyword("SORT BY");
4600            self.write_space();
4601            for (i, ord) in sort_by.expressions.iter().enumerate() {
4602                if i > 0 {
4603                    self.write(", ");
4604                }
4605                self.generate_ordered(ord)?;
4606            }
4607        }
4608        // CLUSTER BY (Hive/Spark)
4609        if let Some(cluster_by) = &except.cluster_by {
4610            self.write_space();
4611            self.write_keyword("CLUSTER BY");
4612            self.write_space();
4613            for (i, ord) in cluster_by.expressions.iter().enumerate() {
4614                if i > 0 {
4615                    self.write(", ");
4616                }
4617                self.generate_ordered(ord)?;
4618            }
4619        }
4620        Ok(())
4621    }
4622
4623    fn generate_insert(&mut self, insert: &Insert) -> Result<()> {
4624        // For TSQL/Fabric/Spark/Hive/Databricks, CTEs must be prepended before INSERT
4625        let prepend_query_cte = if insert.with.is_none() {
4626            use crate::dialects::DialectType;
4627            let should_prepend = matches!(self.config.dialect,
4628                Some(DialectType::TSQL) | Some(DialectType::Fabric)
4629                | Some(DialectType::Spark)
4630                | Some(DialectType::Databricks) | Some(DialectType::Hive)
4631            );
4632            if should_prepend {
4633                if let Some(Expression::Select(select)) = &insert.query {
4634                    select.with.clone()
4635                } else {
4636                    None
4637                }
4638            } else {
4639                None
4640            }
4641        } else {
4642            None
4643        };
4644
4645        // Output WITH clause if on INSERT (e.g., WITH ... INSERT INTO ...)
4646        if let Some(with) = &insert.with {
4647            self.generate_with(with)?;
4648            self.write_space();
4649        } else if let Some(with) = &prepend_query_cte {
4650            self.generate_with(with)?;
4651            self.write_space();
4652        }
4653
4654        // Output leading comments before INSERT
4655        for comment in &insert.leading_comments {
4656            self.write(comment);
4657            self.write(" ");
4658        }
4659
4660        // Handle directory insert (INSERT OVERWRITE DIRECTORY)
4661        if let Some(dir) = &insert.directory {
4662            self.write_keyword("INSERT OVERWRITE");
4663            if dir.local {
4664                self.write_space();
4665                self.write_keyword("LOCAL");
4666            }
4667            self.write_space();
4668            self.write_keyword("DIRECTORY");
4669            self.write_space();
4670            self.write("'");
4671            self.write(&dir.path);
4672            self.write("'");
4673
4674            // ROW FORMAT clause
4675            if let Some(row_format) = &dir.row_format {
4676                self.write_space();
4677                self.write_keyword("ROW FORMAT");
4678                if row_format.delimited {
4679                    self.write_space();
4680                    self.write_keyword("DELIMITED");
4681                }
4682                if let Some(val) = &row_format.fields_terminated_by {
4683                    self.write_space();
4684                    self.write_keyword("FIELDS TERMINATED BY");
4685                    self.write_space();
4686                    self.write("'");
4687                    self.write(val);
4688                    self.write("'");
4689                }
4690                if let Some(val) = &row_format.collection_items_terminated_by {
4691                    self.write_space();
4692                    self.write_keyword("COLLECTION ITEMS TERMINATED BY");
4693                    self.write_space();
4694                    self.write("'");
4695                    self.write(val);
4696                    self.write("'");
4697                }
4698                if let Some(val) = &row_format.map_keys_terminated_by {
4699                    self.write_space();
4700                    self.write_keyword("MAP KEYS TERMINATED BY");
4701                    self.write_space();
4702                    self.write("'");
4703                    self.write(val);
4704                    self.write("'");
4705                }
4706                if let Some(val) = &row_format.lines_terminated_by {
4707                    self.write_space();
4708                    self.write_keyword("LINES TERMINATED BY");
4709                    self.write_space();
4710                    self.write("'");
4711                    self.write(val);
4712                    self.write("'");
4713                }
4714                if let Some(val) = &row_format.null_defined_as {
4715                    self.write_space();
4716                    self.write_keyword("NULL DEFINED AS");
4717                    self.write_space();
4718                    self.write("'");
4719                    self.write(val);
4720                    self.write("'");
4721                }
4722            }
4723
4724            // STORED AS clause
4725            if let Some(format) = &dir.stored_as {
4726                self.write_space();
4727                self.write_keyword("STORED AS");
4728                self.write_space();
4729                self.write_keyword(format);
4730            }
4731
4732            // Query (SELECT statement)
4733            if let Some(query) = &insert.query {
4734                self.write_space();
4735                self.generate_expression(query)?;
4736            }
4737
4738            return Ok(());
4739        }
4740
4741        if insert.is_replace {
4742            // MySQL/SQLite REPLACE INTO statement
4743            self.write_keyword("REPLACE INTO");
4744        } else if insert.overwrite {
4745            // Use dialect-specific INSERT OVERWRITE format
4746            self.write_keyword("INSERT");
4747            // Output hint if present (Oracle: INSERT /*+ APPEND */ INTO)
4748            if let Some(ref hint) = insert.hint {
4749                self.generate_hint(hint)?;
4750            }
4751            self.write(&self.config.insert_overwrite.to_uppercase());
4752        } else if let Some(ref action) = insert.conflict_action {
4753            // SQLite conflict action: INSERT OR ABORT|FAIL|IGNORE|REPLACE|ROLLBACK INTO
4754            self.write_keyword("INSERT OR");
4755            self.write_space();
4756            self.write_keyword(action);
4757            self.write_space();
4758            self.write_keyword("INTO");
4759        } else if insert.ignore {
4760            // MySQL INSERT IGNORE syntax
4761            self.write_keyword("INSERT IGNORE INTO");
4762        } else {
4763            self.write_keyword("INSERT");
4764            // Output hint if present (Oracle: INSERT /*+ APPEND */ INTO)
4765            if let Some(ref hint) = insert.hint {
4766                self.generate_hint(hint)?;
4767            }
4768            self.write_space();
4769            self.write_keyword("INTO");
4770        }
4771        // ClickHouse: INSERT INTO FUNCTION func_name(args...)
4772        if let Some(ref func) = insert.function_target {
4773            self.write_space();
4774            self.write_keyword("FUNCTION");
4775            self.write_space();
4776            self.generate_expression(func)?;
4777        } else {
4778            self.write_space();
4779            self.generate_table(&insert.table)?;
4780        }
4781
4782        // Table alias (PostgreSQL: INSERT INTO table AS t(...), Oracle: INSERT INTO table t ...)
4783        if let Some(ref alias) = insert.alias {
4784            self.write_space();
4785            if insert.alias_explicit_as {
4786                self.write_keyword("AS");
4787                self.write_space();
4788            }
4789            self.generate_identifier(alias)?;
4790        }
4791
4792        // IF EXISTS clause (Hive)
4793        if insert.if_exists {
4794            self.write_space();
4795            self.write_keyword("IF EXISTS");
4796        }
4797
4798        // REPLACE WHERE clause (Databricks)
4799        if let Some(ref replace_where) = insert.replace_where {
4800            self.write_space();
4801            self.write_keyword("REPLACE WHERE");
4802            self.write_space();
4803            self.generate_expression(replace_where)?;
4804        }
4805
4806        // Generate PARTITION clause if present
4807        if !insert.partition.is_empty() {
4808            self.write_space();
4809            self.write_keyword("PARTITION");
4810            self.write("(");
4811            for (i, (col, val)) in insert.partition.iter().enumerate() {
4812                if i > 0 {
4813                    self.write(", ");
4814                }
4815                self.generate_identifier(col)?;
4816                if let Some(v) = val {
4817                    self.write(" = ");
4818                    self.generate_expression(v)?;
4819                }
4820            }
4821            self.write(")");
4822        }
4823
4824        // ClickHouse: PARTITION BY expr
4825        if let Some(ref partition_by) = insert.partition_by {
4826            self.write_space();
4827            self.write_keyword("PARTITION BY");
4828            self.write_space();
4829            self.generate_expression(partition_by)?;
4830        }
4831
4832        // ClickHouse: SETTINGS key = val, ...
4833        if !insert.settings.is_empty() {
4834            self.write_space();
4835            self.write_keyword("SETTINGS");
4836            self.write_space();
4837            for (i, setting) in insert.settings.iter().enumerate() {
4838                if i > 0 {
4839                    self.write(", ");
4840                }
4841                self.generate_expression(setting)?;
4842            }
4843        }
4844
4845        if !insert.columns.is_empty() {
4846            if insert.alias.is_some() && insert.alias_explicit_as {
4847                // No space when explicit AS alias is present: INSERT INTO table AS t(a, b, c)
4848                self.write("(");
4849            } else {
4850                // Space for implicit alias or no alias: INSERT INTO dest d (i, value)
4851                self.write(" (");
4852            }
4853            for (i, col) in insert.columns.iter().enumerate() {
4854                if i > 0 {
4855                    self.write(", ");
4856                }
4857                self.generate_identifier(col)?;
4858            }
4859            self.write(")");
4860        }
4861
4862        // OUTPUT clause (TSQL)
4863        if let Some(ref output) = insert.output {
4864            self.generate_output_clause(output)?;
4865        }
4866
4867        // BY NAME modifier (DuckDB)
4868        if insert.by_name {
4869            self.write_space();
4870            self.write_keyword("BY NAME");
4871        }
4872
4873        if insert.default_values {
4874            self.write_space();
4875            self.write_keyword("DEFAULT VALUES");
4876        } else if let Some(query) = &insert.query {
4877            if self.config.pretty {
4878                self.write_newline();
4879            } else {
4880                self.write_space();
4881            }
4882            // If we prepended CTEs from nested SELECT (TSQL), strip the WITH from SELECT
4883            if prepend_query_cte.is_some() {
4884                if let Expression::Select(select) = query {
4885                    let mut select_no_with = select.clone();
4886                    select_no_with.with = None;
4887                    self.generate_select(&select_no_with)?;
4888                } else {
4889                    self.generate_expression(query)?;
4890                }
4891            } else {
4892                self.generate_expression(query)?;
4893            }
4894        } else if !insert.values.is_empty() {
4895            if self.config.pretty {
4896                // Pretty printing: VALUES on new line, each tuple indented
4897                self.write_newline();
4898                self.write_keyword("VALUES");
4899                self.write_newline();
4900                self.indent_level += 1;
4901                for (i, row) in insert.values.iter().enumerate() {
4902                    if i > 0 {
4903                        self.write(",");
4904                        self.write_newline();
4905                    }
4906                    self.write_indent();
4907                    self.write("(");
4908                    for (j, val) in row.iter().enumerate() {
4909                        if j > 0 {
4910                            self.write(", ");
4911                        }
4912                        self.generate_expression(val)?;
4913                    }
4914                    self.write(")");
4915                }
4916                self.indent_level -= 1;
4917            } else {
4918                // Non-pretty: single line
4919                self.write_space();
4920                self.write_keyword("VALUES");
4921                for (i, row) in insert.values.iter().enumerate() {
4922                    if i > 0 {
4923                        self.write(",");
4924                    }
4925                    self.write(" (");
4926                    for (j, val) in row.iter().enumerate() {
4927                        if j > 0 {
4928                            self.write(", ");
4929                        }
4930                        self.generate_expression(val)?;
4931                    }
4932                    self.write(")");
4933                }
4934            }
4935        }
4936
4937        // Source table (Hive/Spark): INSERT OVERWRITE TABLE target TABLE source
4938        if let Some(ref source) = insert.source {
4939            self.write_space();
4940            self.write_keyword("TABLE");
4941            self.write_space();
4942            self.generate_expression(source)?;
4943        }
4944
4945        // Source alias (MySQL: VALUES (...) AS new_data)
4946        if let Some(alias) = &insert.source_alias {
4947            self.write_space();
4948            self.write_keyword("AS");
4949            self.write_space();
4950            self.generate_identifier(alias)?;
4951        }
4952
4953        // ON CONFLICT clause (Materialize doesn't support ON CONFLICT)
4954        if let Some(on_conflict) = &insert.on_conflict {
4955            if !matches!(self.config.dialect, Some(DialectType::Materialize)) {
4956                self.write_space();
4957                self.generate_expression(on_conflict)?;
4958            }
4959        }
4960
4961        // RETURNING clause
4962        if !insert.returning.is_empty() {
4963            self.write_space();
4964            self.write_keyword("RETURNING");
4965            self.write_space();
4966            for (i, expr) in insert.returning.iter().enumerate() {
4967                if i > 0 {
4968                    self.write(", ");
4969                }
4970                self.generate_expression(expr)?;
4971            }
4972        }
4973
4974        Ok(())
4975    }
4976
4977    fn generate_update(&mut self, update: &Update) -> Result<()> {
4978        // Output leading comments before UPDATE
4979        for comment in &update.leading_comments {
4980            self.write(comment);
4981            self.write(" ");
4982        }
4983
4984        // WITH clause (CTEs)
4985        if let Some(ref with) = update.with {
4986            self.generate_with(with)?;
4987            self.write_space();
4988        }
4989
4990        self.write_keyword("UPDATE");
4991        self.write_space();
4992        self.generate_table(&update.table)?;
4993
4994        let mysql_like_update_from = matches!(
4995            self.config.dialect,
4996            Some(DialectType::MySQL) | Some(DialectType::SingleStore)
4997        ) && update.from_clause.is_some();
4998
4999        let mut set_pairs = update.set.clone();
5000
5001        // MySQL-style UPDATE doesn't support FROM after SET. Convert FROM tables to JOIN ... ON TRUE.
5002        let mut pre_set_joins = update.table_joins.clone();
5003        if mysql_like_update_from {
5004            let target_name = update.table.alias
5005                .as_ref()
5006                .map(|a| a.name.clone())
5007                .unwrap_or_else(|| update.table.name.name.clone());
5008
5009            for (col, _) in &mut set_pairs {
5010                if !col.name.contains('.') {
5011                    col.name = format!("{}.{}", target_name, col.name);
5012                }
5013            }
5014
5015            if let Some(from_clause) = &update.from_clause {
5016                for table_expr in &from_clause.expressions {
5017                    pre_set_joins.push(crate::expressions::Join {
5018                        this: table_expr.clone(),
5019                        on: Some(Expression::Boolean(crate::expressions::BooleanLiteral { value: true })),
5020                        using: Vec::new(),
5021                        kind: crate::expressions::JoinKind::Inner,
5022                        use_inner_keyword: false,
5023                        use_outer_keyword: false,
5024                        deferred_condition: false,
5025                        join_hint: None,
5026                        match_condition: None,
5027                        pivots: Vec::new(),
5028                    });
5029                }
5030            }
5031            for join in &update.from_joins {
5032                let mut join = join.clone();
5033                if join.on.is_none() && join.using.is_empty() {
5034                    join.on = Some(Expression::Boolean(crate::expressions::BooleanLiteral { value: true }));
5035                }
5036                pre_set_joins.push(join);
5037            }
5038        }
5039
5040        // Extra tables for multi-table UPDATE (MySQL syntax)
5041        for extra_table in &update.extra_tables {
5042            self.write(", ");
5043            self.generate_table(extra_table)?;
5044        }
5045
5046        // JOINs attached to the table list (MySQL multi-table syntax)
5047        for join in &pre_set_joins {
5048            // generate_join already adds a leading space
5049            self.generate_join(join)?;
5050        }
5051
5052        // Teradata: FROM clause comes before SET
5053        let teradata_from_before_set = matches!(self.config.dialect, Some(DialectType::Teradata));
5054        if teradata_from_before_set && !mysql_like_update_from {
5055            if let Some(ref from_clause) = update.from_clause {
5056                self.write_space();
5057                self.write_keyword("FROM");
5058                self.write_space();
5059                for (i, table_expr) in from_clause.expressions.iter().enumerate() {
5060                    if i > 0 {
5061                        self.write(", ");
5062                    }
5063                    self.generate_expression(table_expr)?;
5064                }
5065            }
5066            for join in &update.from_joins {
5067                self.generate_join(join)?;
5068            }
5069        }
5070
5071        self.write_space();
5072        self.write_keyword("SET");
5073        self.write_space();
5074
5075        for (i, (col, val)) in set_pairs.iter().enumerate() {
5076            if i > 0 {
5077                self.write(", ");
5078            }
5079            self.generate_identifier(col)?;
5080            self.write(" = ");
5081            self.generate_expression(val)?;
5082        }
5083
5084        // OUTPUT clause (TSQL)
5085        if let Some(ref output) = update.output {
5086            self.generate_output_clause(output)?;
5087        }
5088
5089        // FROM clause (after SET for non-Teradata, non-MySQL dialects)
5090        if !mysql_like_update_from && !teradata_from_before_set {
5091            if let Some(ref from_clause) = update.from_clause {
5092                self.write_space();
5093                self.write_keyword("FROM");
5094                self.write_space();
5095                // Generate each table in the FROM clause
5096                for (i, table_expr) in from_clause.expressions.iter().enumerate() {
5097                    if i > 0 {
5098                        self.write(", ");
5099                    }
5100                    self.generate_expression(table_expr)?;
5101                }
5102            }
5103        }
5104
5105        if !mysql_like_update_from && !teradata_from_before_set {
5106            // JOINs after FROM clause (PostgreSQL, Snowflake, SQL Server syntax)
5107            for join in &update.from_joins {
5108                self.generate_join(join)?;
5109            }
5110        }
5111
5112        if let Some(where_clause) = &update.where_clause {
5113            self.write_space();
5114            self.write_keyword("WHERE");
5115            self.write_space();
5116            self.generate_expression(&where_clause.this)?;
5117        }
5118
5119        // RETURNING clause
5120        if !update.returning.is_empty() {
5121            self.write_space();
5122            self.write_keyword("RETURNING");
5123            self.write_space();
5124            for (i, expr) in update.returning.iter().enumerate() {
5125                if i > 0 {
5126                    self.write(", ");
5127                }
5128                self.generate_expression(expr)?;
5129            }
5130        }
5131
5132        // ORDER BY clause (MySQL)
5133        if let Some(ref order_by) = update.order_by {
5134            self.write_space();
5135            self.generate_order_by(order_by)?;
5136        }
5137
5138        // LIMIT clause (MySQL)
5139        if let Some(ref limit) = update.limit {
5140            self.write_space();
5141            self.write_keyword("LIMIT");
5142            self.write_space();
5143            self.generate_expression(limit)?;
5144        }
5145
5146        Ok(())
5147    }
5148
5149    fn generate_delete(&mut self, delete: &Delete) -> Result<()> {
5150        // Output WITH clause if present
5151        if let Some(with) = &delete.with {
5152            self.generate_with(with)?;
5153            self.write_space();
5154        }
5155
5156        // Output leading comments before DELETE
5157        for comment in &delete.leading_comments {
5158            self.write(comment);
5159            self.write(" ");
5160        }
5161
5162        // MySQL multi-table DELETE or TSQL DELETE with OUTPUT before FROM
5163        if !delete.tables.is_empty() && !delete.tables_from_using {
5164            // DELETE t1[, t2] [OUTPUT ...] FROM ... syntax (tables before FROM)
5165            self.write_keyword("DELETE");
5166            self.write_space();
5167            for (i, tbl) in delete.tables.iter().enumerate() {
5168                if i > 0 {
5169                    self.write(", ");
5170                }
5171                self.generate_table(tbl)?;
5172            }
5173            // TSQL: OUTPUT clause between target table and FROM
5174            if let Some(ref output) = delete.output {
5175                self.generate_output_clause(output)?;
5176            }
5177            self.write_space();
5178            self.write_keyword("FROM");
5179            self.write_space();
5180            self.generate_table(&delete.table)?;
5181        } else if !delete.tables.is_empty() && delete.tables_from_using {
5182            // DELETE FROM t1, t2 USING ... syntax (tables after FROM)
5183            self.write_keyword("DELETE FROM");
5184            self.write_space();
5185            for (i, tbl) in delete.tables.iter().enumerate() {
5186                if i > 0 {
5187                    self.write(", ");
5188                }
5189                self.generate_table(tbl)?;
5190            }
5191        } else if delete.no_from && matches!(self.config.dialect, Some(DialectType::BigQuery)) {
5192            // BigQuery-style DELETE without FROM keyword
5193            self.write_keyword("DELETE");
5194            self.write_space();
5195            self.generate_table(&delete.table)?;
5196        } else {
5197            self.write_keyword("DELETE FROM");
5198            self.write_space();
5199            self.generate_table(&delete.table)?;
5200        }
5201
5202        // ClickHouse: ON CLUSTER clause
5203        if let Some(ref on_cluster) = delete.on_cluster {
5204            self.write_space();
5205            self.generate_on_cluster(on_cluster)?;
5206        }
5207
5208        // FORCE INDEX hint (MySQL)
5209        if let Some(ref idx) = delete.force_index {
5210            self.write_space();
5211            self.write_keyword("FORCE INDEX");
5212            self.write(" (");
5213            self.write(idx);
5214            self.write(")");
5215        }
5216
5217        // Optional alias
5218        if let Some(ref alias) = delete.alias {
5219            self.write_space();
5220            if delete.alias_explicit_as || matches!(self.config.dialect, Some(DialectType::BigQuery)) {
5221                self.write_keyword("AS");
5222                self.write_space();
5223            }
5224            self.generate_identifier(alias)?;
5225        }
5226
5227        // JOINs (MySQL multi-table) - when NOT tables_from_using, JOINs come before USING
5228        if !delete.tables_from_using {
5229            for join in &delete.joins {
5230                self.generate_join(join)?;
5231            }
5232        }
5233
5234        // USING clause (PostgreSQL/DuckDB/MySQL)
5235        if !delete.using.is_empty() {
5236            self.write_space();
5237            self.write_keyword("USING");
5238            for (i, table) in delete.using.iter().enumerate() {
5239                if i > 0 {
5240                    self.write(",");
5241                }
5242                self.write_space();
5243                // Check if the table has subquery hints (DuckDB USING with subquery)
5244                if !table.hints.is_empty() && table.name.is_empty() {
5245                    // Subquery in USING: (VALUES ...) AS alias(cols)
5246                    self.generate_expression(&table.hints[0])?;
5247                    if let Some(ref alias) = table.alias {
5248                        self.write_space();
5249                        if table.alias_explicit_as {
5250                            self.write_keyword("AS");
5251                            self.write_space();
5252                        }
5253                        self.generate_identifier(alias)?;
5254                        if !table.column_aliases.is_empty() {
5255                            self.write("(");
5256                            for (j, col_alias) in table.column_aliases.iter().enumerate() {
5257                                if j > 0 {
5258                                    self.write(", ");
5259                                }
5260                                self.generate_identifier(col_alias)?;
5261                            }
5262                            self.write(")");
5263                        }
5264                    }
5265                } else {
5266                    self.generate_table(table)?;
5267                }
5268            }
5269        }
5270
5271        // JOINs (MySQL multi-table) - when tables_from_using, JOINs come after USING
5272        if delete.tables_from_using {
5273            for join in &delete.joins {
5274                self.generate_join(join)?;
5275            }
5276        }
5277
5278        // OUTPUT clause (TSQL) - only if not already emitted in the early position
5279        let output_already_emitted = !delete.tables.is_empty() && !delete.tables_from_using && delete.output.is_some();
5280        if !output_already_emitted {
5281            if let Some(ref output) = delete.output {
5282                self.generate_output_clause(output)?;
5283            }
5284        }
5285
5286        if let Some(where_clause) = &delete.where_clause {
5287            self.write_space();
5288            self.write_keyword("WHERE");
5289            self.write_space();
5290            self.generate_expression(&where_clause.this)?;
5291        }
5292
5293        // ORDER BY clause (MySQL)
5294        if let Some(ref order_by) = delete.order_by {
5295            self.write_space();
5296            self.generate_order_by(order_by)?;
5297        }
5298
5299        // LIMIT clause (MySQL)
5300        if let Some(ref limit) = delete.limit {
5301            self.write_space();
5302            self.write_keyword("LIMIT");
5303            self.write_space();
5304            self.generate_expression(limit)?;
5305        }
5306
5307        // RETURNING clause (PostgreSQL)
5308        if !delete.returning.is_empty() {
5309            self.write_space();
5310            self.write_keyword("RETURNING");
5311            self.write_space();
5312            for (i, expr) in delete.returning.iter().enumerate() {
5313                if i > 0 {
5314                    self.write(", ");
5315                }
5316                self.generate_expression(expr)?;
5317            }
5318        }
5319
5320        Ok(())
5321    }
5322
5323    // ==================== DDL Generation ====================
5324
5325    fn generate_create_table(&mut self, ct: &CreateTable) -> Result<()> {
5326        // Athena: Determine if this is Hive-style DDL or Trino-style DML
5327        // CREATE TABLE AS SELECT uses Trino (double quotes)
5328        // CREATE TABLE (without AS SELECT) and CREATE EXTERNAL TABLE use Hive (backticks)
5329        let saved_athena_hive_context = self.athena_hive_context;
5330        let is_clickhouse = matches!(self.config.dialect, Some(DialectType::ClickHouse));
5331        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
5332            // Use Hive context if:
5333            // 1. It's an EXTERNAL table, OR
5334            // 2. There's no AS SELECT clause
5335            let is_external = ct.table_modifier.as_ref().map(|m| m.eq_ignore_ascii_case("EXTERNAL")).unwrap_or(false);
5336            let has_as_select = ct.as_select.is_some();
5337            self.athena_hive_context = is_external || !has_as_select;
5338        }
5339
5340        // TSQL: Convert CREATE TABLE AS SELECT to SELECT * INTO table FROM (subquery) AS temp
5341        if matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL)) {
5342            if let Some(ref query) = ct.as_select {
5343                // Output WITH CTE clause if present
5344                if let Some(with_cte) = &ct.with_cte {
5345                    self.generate_with(with_cte)?;
5346                    self.write_space();
5347                }
5348
5349                // Generate: SELECT * INTO [table] FROM (subquery) AS temp
5350                self.write_keyword("SELECT");
5351                self.write(" * ");
5352                self.write_keyword("INTO");
5353                self.write_space();
5354
5355                // If temporary, prefix with # for TSQL temp table
5356                if ct.temporary {
5357                    self.write("#");
5358                }
5359                self.generate_table(&ct.name)?;
5360
5361                self.write_space();
5362                self.write_keyword("FROM");
5363                self.write(" (");
5364                // For TSQL, add aliases to select columns to preserve column names
5365                let aliased_query = Self::add_column_aliases_to_query(query.clone());
5366                self.generate_expression(&aliased_query)?;
5367                self.write(") ");
5368                self.write_keyword("AS");
5369                self.write(" temp");
5370                return Ok(());
5371            }
5372        }
5373
5374        // Output WITH CTE clause if present
5375        if let Some(with_cte) = &ct.with_cte {
5376            self.generate_with(with_cte)?;
5377            self.write_space();
5378        }
5379
5380        // Output leading comments before CREATE
5381        for comment in &ct.leading_comments {
5382            self.write(comment);
5383            self.write(" ");
5384        }
5385        self.write_keyword("CREATE");
5386
5387        if ct.or_replace {
5388            self.write_space();
5389            self.write_keyword("OR REPLACE");
5390        }
5391
5392        if ct.temporary {
5393            self.write_space();
5394            // Oracle uses GLOBAL TEMPORARY TABLE syntax
5395            if matches!(self.config.dialect, Some(DialectType::Oracle)) {
5396                self.write_keyword("GLOBAL TEMPORARY");
5397            } else {
5398                self.write_keyword("TEMPORARY");
5399            }
5400        }
5401
5402        // Table modifier: DYNAMIC, ICEBERG, EXTERNAL, HYBRID, TRANSIENT
5403        let is_dictionary = ct
5404            .table_modifier
5405            .as_ref()
5406            .map(|m| m.eq_ignore_ascii_case("DICTIONARY"))
5407            .unwrap_or(false);
5408        if let Some(ref modifier) = ct.table_modifier {
5409            // TRANSIENT is Snowflake-specific - skip for other dialects
5410            let skip_transient = modifier.eq_ignore_ascii_case("TRANSIENT")
5411                && !matches!(self.config.dialect, Some(DialectType::Snowflake) | None);
5412            // Teradata-specific modifiers: VOLATILE, SET, MULTISET, SET TABLE combinations
5413            let is_teradata_modifier = modifier.eq_ignore_ascii_case("VOLATILE")
5414                || modifier.eq_ignore_ascii_case("SET")
5415                || modifier.eq_ignore_ascii_case("MULTISET")
5416                || modifier.to_uppercase().contains("VOLATILE")
5417                || modifier.to_uppercase().starts_with("SET ")
5418                || modifier.to_uppercase().starts_with("MULTISET ");
5419            let skip_teradata = is_teradata_modifier
5420                && !matches!(self.config.dialect, Some(DialectType::Teradata));
5421            if !skip_transient && !skip_teradata {
5422                self.write_space();
5423                self.write_keyword(modifier);
5424            }
5425        }
5426
5427        if !is_dictionary {
5428            self.write_space();
5429            self.write_keyword("TABLE");
5430        }
5431
5432        if ct.if_not_exists {
5433            self.write_space();
5434            self.write_keyword("IF NOT EXISTS");
5435        }
5436
5437        self.write_space();
5438        self.generate_table(&ct.name)?;
5439
5440        // ClickHouse: ON CLUSTER clause
5441        if let Some(ref on_cluster) = ct.on_cluster {
5442            self.write_space();
5443            self.generate_on_cluster(on_cluster)?;
5444        }
5445
5446        // Teradata: options after table name before column list (comma-separated)
5447        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Teradata))
5448            && !ct.teradata_post_name_options.is_empty()
5449        {
5450            for opt in &ct.teradata_post_name_options {
5451                self.write(", ");
5452                self.write(opt);
5453            }
5454        }
5455
5456        // Snowflake: COPY GRANTS clause
5457        if ct.copy_grants {
5458            self.write_space();
5459            self.write_keyword("COPY GRANTS");
5460        }
5461
5462        // Snowflake: USING TEMPLATE clause (before columns or AS SELECT)
5463        if let Some(ref using_template) = ct.using_template {
5464            self.write_space();
5465            self.write_keyword("USING TEMPLATE");
5466            self.write_space();
5467            self.generate_expression(using_template)?;
5468            return Ok(());
5469        }
5470
5471        // Handle [SHALLOW | DEEP] CLONE/COPY source_table [AT(...) | BEFORE(...)]
5472        if let Some(ref clone_source) = ct.clone_source {
5473            self.write_space();
5474            if ct.is_copy && self.config.supports_table_copy {
5475                // BigQuery uses COPY
5476                self.write_keyword("COPY");
5477            } else if ct.shallow_clone {
5478                self.write_keyword("SHALLOW CLONE");
5479            } else {
5480                self.write_keyword("CLONE");
5481            }
5482            self.write_space();
5483            self.generate_table(clone_source)?;
5484            // Generate AT/BEFORE time travel clause (stored as Raw expression)
5485            if let Some(ref at_clause) = ct.clone_at_clause {
5486                self.write_space();
5487                self.generate_expression(at_clause)?;
5488            }
5489            return Ok(());
5490        }
5491
5492        // Handle PARTITION OF property
5493        // Output order: PARTITION OF <table> (<columns/constraints>) FOR VALUES ...
5494        // Columns/constraints must appear BETWEEN the table name and the partition bound spec
5495        if let Some(ref partition_of) = ct.partition_of {
5496            self.write_space();
5497
5498            // Extract the PartitionedOfProperty parts to generate them separately
5499            if let Expression::PartitionedOfProperty(ref pop) = partition_of {
5500                // Output: PARTITION OF <table>
5501                self.write_keyword("PARTITION OF");
5502                self.write_space();
5503                self.generate_expression(&pop.this)?;
5504
5505                // Output columns/constraints if present (e.g., (unitsales DEFAULT 0) or (CONSTRAINT ...))
5506                if !ct.columns.is_empty() || !ct.constraints.is_empty() {
5507                    self.write(" (");
5508                    let mut first = true;
5509                    for col in &ct.columns {
5510                        if !first {
5511                            self.write(", ");
5512                        }
5513                        first = false;
5514                        self.generate_column_def(col)?;
5515                    }
5516                    for constraint in &ct.constraints {
5517                        if !first {
5518                            self.write(", ");
5519                        }
5520                        first = false;
5521                        self.generate_table_constraint(constraint)?;
5522                    }
5523                    self.write(")");
5524                }
5525
5526                // Output partition bound spec: FOR VALUES ... or DEFAULT
5527                if let Expression::PartitionBoundSpec(_) = pop.expression.as_ref() {
5528                    self.write_space();
5529                    self.write_keyword("FOR VALUES");
5530                    self.write_space();
5531                    self.generate_expression(&pop.expression)?;
5532                } else {
5533                    self.write_space();
5534                    self.write_keyword("DEFAULT");
5535                }
5536            } else {
5537                // Fallback: generate the whole expression if it's not a PartitionedOfProperty
5538                self.generate_expression(partition_of)?;
5539
5540                // Output columns/constraints if present
5541                if !ct.columns.is_empty() || !ct.constraints.is_empty() {
5542                    self.write(" (");
5543                    let mut first = true;
5544                    for col in &ct.columns {
5545                        if !first {
5546                            self.write(", ");
5547                        }
5548                        first = false;
5549                        self.generate_column_def(col)?;
5550                    }
5551                    for constraint in &ct.constraints {
5552                        if !first {
5553                            self.write(", ");
5554                        }
5555                        first = false;
5556                        self.generate_table_constraint(constraint)?;
5557                    }
5558                    self.write(")");
5559                }
5560            }
5561
5562            // Output table properties (e.g., PARTITION BY RANGE(population))
5563            for prop in &ct.properties {
5564                self.write_space();
5565                self.generate_expression(prop)?;
5566            }
5567
5568            return Ok(());
5569        }
5570
5571        // SQLite: Inline single-column PRIMARY KEY constraints into column definition
5572        // This matches Python sqlglot's behavior for SQLite dialect
5573        self.sqlite_inline_pk_columns.clear();
5574        if matches!(self.config.dialect, Some(crate::dialects::DialectType::SQLite)) {
5575            for constraint in &ct.constraints {
5576                if let TableConstraint::PrimaryKey { columns, name, .. } = constraint {
5577                    // Only inline if: single column, no constraint name, and column exists in table
5578                    if columns.len() == 1 && name.is_none() {
5579                        let pk_col_name = columns[0].name.to_lowercase();
5580                        // Check if this column exists in the table
5581                        if ct.columns.iter().any(|c| c.name.name.to_lowercase() == pk_col_name) {
5582                            self.sqlite_inline_pk_columns.insert(pk_col_name);
5583                        }
5584                    }
5585                }
5586            }
5587        }
5588
5589        // Output columns if present (even for CTAS with columns)
5590        if !ct.columns.is_empty() {
5591            if self.config.pretty {
5592                // Pretty print: each column on new line
5593                self.write(" (");
5594                self.write_newline();
5595                self.indent_level += 1;
5596                for (i, col) in ct.columns.iter().enumerate() {
5597                    if i > 0 {
5598                        self.write(",");
5599                        self.write_newline();
5600                    }
5601                    self.write_indent();
5602                    self.generate_column_def(col)?;
5603                }
5604                // Table constraints (skip inlined PRIMARY KEY for SQLite)
5605                for constraint in &ct.constraints {
5606                    // Skip single-column PRIMARY KEY that was inlined for SQLite
5607                    if let TableConstraint::PrimaryKey { columns, name, .. } = constraint {
5608                        if columns.len() == 1 && name.is_none() &&
5609                           self.sqlite_inline_pk_columns.contains(&columns[0].name.to_lowercase()) {
5610                            continue;
5611                        }
5612                    }
5613                    self.write(",");
5614                    self.write_newline();
5615                    self.write_indent();
5616                    self.generate_table_constraint(constraint)?;
5617                }
5618                self.indent_level -= 1;
5619                self.write_newline();
5620                self.write(")");
5621            } else {
5622                self.write(" (");
5623                for (i, col) in ct.columns.iter().enumerate() {
5624                    if i > 0 {
5625                        self.write(", ");
5626                    }
5627                    self.generate_column_def(col)?;
5628                }
5629                // Table constraints (skip inlined PRIMARY KEY for SQLite)
5630                let mut first_constraint = true;
5631                for constraint in &ct.constraints {
5632                    // Skip single-column PRIMARY KEY that was inlined for SQLite
5633                    if let TableConstraint::PrimaryKey { columns, name, .. } = constraint {
5634                        if columns.len() == 1 && name.is_none() &&
5635                           self.sqlite_inline_pk_columns.contains(&columns[0].name.to_lowercase()) {
5636                            continue;
5637                        }
5638                    }
5639                    if first_constraint {
5640                        self.write(", ");
5641                        first_constraint = false;
5642                    } else {
5643                        self.write(", ");
5644                    }
5645                    self.generate_table_constraint(constraint)?;
5646                }
5647                self.write(")");
5648            }
5649        } else if !ct.constraints.is_empty() {
5650            // No columns but constraints exist (e.g., CREATE TABLE A LIKE B or CREATE TABLE A TAG (...))
5651            let has_like_only = ct.constraints.iter().all(|c| matches!(c, TableConstraint::Like { .. }));
5652            let has_tags_only = ct.constraints.iter().all(|c| matches!(c, TableConstraint::Tags(_)));
5653            // MySQL: CREATE TABLE A LIKE B (no parens)
5654            // Snowflake: CREATE TABLE A TAG (...) (no outer parens, but TAG has its own)
5655            // PostgreSQL and others: CREATE TABLE A (LIKE B INCLUDING ALL) (with parens)
5656            let is_mysql = matches!(self.config.dialect, Some(crate::dialects::DialectType::MySQL) | Some(crate::dialects::DialectType::SingleStore) | Some(crate::dialects::DialectType::TiDB));
5657            let use_parens = !(has_like_only && is_mysql) && !has_tags_only;
5658            if self.config.pretty && use_parens {
5659                self.write(" (");
5660                self.write_newline();
5661                self.indent_level += 1;
5662                for (i, constraint) in ct.constraints.iter().enumerate() {
5663                    if i > 0 {
5664                        self.write(",");
5665                        self.write_newline();
5666                    }
5667                    self.write_indent();
5668                    self.generate_table_constraint(constraint)?;
5669                }
5670                self.indent_level -= 1;
5671                self.write_newline();
5672                self.write(")");
5673            } else {
5674                if use_parens {
5675                    self.write(" (");
5676                } else {
5677                    self.write_space();
5678                }
5679                for (i, constraint) in ct.constraints.iter().enumerate() {
5680                    if i > 0 {
5681                        self.write(", ");
5682                    }
5683                    self.generate_table_constraint(constraint)?;
5684                }
5685                if use_parens {
5686                    self.write(")");
5687                }
5688            }
5689        }
5690
5691        // TSQL ON filegroup or ON filegroup (partition_column) clause
5692        if let Some(ref on_prop) = ct.on_property {
5693            self.write(" ");
5694            self.write_keyword("ON");
5695            self.write(" ");
5696            self.generate_expression(&on_prop.this)?;
5697        }
5698
5699        // Output SchemaCommentProperty BEFORE WITH properties (Presto/Hive/Spark style)
5700        // For ClickHouse, SchemaCommentProperty goes after AS SELECT, handled later
5701        if !is_clickhouse {
5702            for prop in &ct.properties {
5703                if let Expression::SchemaCommentProperty(_) = prop {
5704                    if self.config.pretty {
5705                        self.write_newline();
5706                    } else {
5707                        self.write_space();
5708                    }
5709                    self.generate_expression(prop)?;
5710                }
5711            }
5712        }
5713
5714        // WITH properties (output after columns if columns exist, otherwise before AS)
5715        if !ct.with_properties.is_empty() {
5716            // Snowflake ICEBERG/DYNAMIC TABLE: output properties inline (space-separated, no WITH wrapper)
5717            let is_snowflake_special_table = matches!(self.config.dialect, Some(crate::dialects::DialectType::Snowflake))
5718                && (ct.table_modifier.as_deref() == Some("ICEBERG") || ct.table_modifier.as_deref() == Some("DYNAMIC"));
5719            if is_snowflake_special_table {
5720                for (key, value) in &ct.with_properties {
5721                    self.write_space();
5722                    self.write(key);
5723                    self.write("=");
5724                    self.write(value);
5725                }
5726            } else if self.config.pretty {
5727                self.write_newline();
5728                self.write_keyword("WITH");
5729                self.write(" (");
5730                self.write_newline();
5731                self.indent_level += 1;
5732                for (i, (key, value)) in ct.with_properties.iter().enumerate() {
5733                    if i > 0 {
5734                        self.write(",");
5735                        self.write_newline();
5736                    }
5737                    self.write_indent();
5738                    self.write(key);
5739                    self.write("=");
5740                    self.write(value);
5741                }
5742                self.indent_level -= 1;
5743                self.write_newline();
5744                self.write(")");
5745            } else {
5746                self.write_space();
5747                self.write_keyword("WITH");
5748                self.write(" (");
5749                for (i, (key, value)) in ct.with_properties.iter().enumerate() {
5750                    if i > 0 {
5751                        self.write(", ");
5752                    }
5753                    self.write(key);
5754                    self.write("=");
5755                    self.write(value);
5756                }
5757                self.write(")");
5758            }
5759        }
5760
5761        let (pre_as_properties, post_as_properties): (Vec<&Expression>, Vec<&Expression>) = if is_clickhouse && ct.as_select.is_some() {
5762            let mut pre = Vec::new();
5763            let mut post = Vec::new();
5764            for prop in &ct.properties {
5765                if matches!(prop, Expression::SchemaCommentProperty(_)) {
5766                    post.push(prop);
5767                } else {
5768                    pre.push(prop);
5769                }
5770            }
5771            (pre, post)
5772        } else {
5773            (ct.properties.iter().collect(), Vec::new())
5774        };
5775
5776        // Table properties like DEFAULT COLLATE (BigQuery), OPTIONS (...), TBLPROPERTIES (...), or PROPERTIES (...)
5777        for prop in pre_as_properties {
5778            // SchemaCommentProperty was already output before WITH properties (except for ClickHouse)
5779            if !is_clickhouse && matches!(prop, Expression::SchemaCommentProperty(_)) {
5780                continue;
5781            }
5782            if self.config.pretty {
5783                self.write_newline();
5784            } else {
5785                self.write_space();
5786            }
5787            // BigQuery: Properties containing OPTIONS should be wrapped with OPTIONS (...)
5788            // Hive: Properties should be wrapped with TBLPROPERTIES (...)
5789            // Doris/StarRocks: Properties should be wrapped with PROPERTIES (...)
5790            if let Expression::Properties(props) = prop {
5791                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));
5792                let is_doris_starrocks = matches!(self.config.dialect, Some(crate::dialects::DialectType::Doris) | Some(crate::dialects::DialectType::StarRocks));
5793                if is_hive_dialect {
5794                    self.generate_tblproperties_clause(&props.expressions)?;
5795                } else if is_doris_starrocks {
5796                    self.generate_properties_clause(&props.expressions)?;
5797                } else {
5798                    self.generate_options_clause(&props.expressions)?;
5799                }
5800            } else {
5801                self.generate_expression(prop)?;
5802            }
5803        }
5804
5805        // Post-table properties like TSQL WITH(SYSTEM_VERSIONING=ON(...)) or Doris PROPERTIES
5806        for prop in &ct.post_table_properties {
5807            if let Expression::WithSystemVersioningProperty(ref svp) = prop {
5808                self.write(" WITH(");
5809                self.generate_system_versioning_content(svp)?;
5810                self.write(")");
5811            } else if let Expression::Properties(props) = prop {
5812                // Doris/StarRocks: PROPERTIES ('key'='value', ...) in post_table_properties
5813                let is_doris_starrocks = matches!(self.config.dialect, Some(crate::dialects::DialectType::Doris) | Some(crate::dialects::DialectType::StarRocks));
5814                self.write_space();
5815                if is_doris_starrocks {
5816                    self.generate_properties_clause(&props.expressions)?;
5817                } else {
5818                    self.generate_options_clause(&props.expressions)?;
5819                }
5820            } else {
5821                self.write_space();
5822                self.generate_expression(prop)?;
5823            }
5824        }
5825
5826        // StarRocks ROLLUP property: ROLLUP (r1(col1, col2), r2(col1))
5827        // Only output for StarRocks target
5828        if let Some(ref rollup) = ct.rollup {
5829            if matches!(self.config.dialect, Some(DialectType::StarRocks)) {
5830                self.write_space();
5831                self.generate_rollup_property(rollup)?;
5832            }
5833        }
5834
5835        // MySQL table options (ENGINE=val, AUTO_INCREMENT=val, etc.)
5836        // Only output for MySQL-compatible dialects; strip for others during transpilation
5837        // COMMENT is also used by Hive/Spark so we selectively preserve it
5838        let is_mysql_compatible = matches!(self.config.dialect,
5839            Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::Doris) | Some(DialectType::StarRocks) | None
5840        );
5841        let is_hive_compatible = matches!(self.config.dialect,
5842            Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Athena)
5843        );
5844        let mysql_pretty_options = self.config.pretty && matches!(self.config.dialect, Some(DialectType::MySQL));
5845        for (key, value) in &ct.mysql_table_options {
5846            // Skip non-MySQL-specific options for non-MySQL targets
5847            let should_output = if is_mysql_compatible {
5848                true
5849            } else if is_hive_compatible && key == "COMMENT" {
5850                true // COMMENT is valid in Hive/Spark table definitions
5851            } else {
5852                false
5853            };
5854            if should_output {
5855                if mysql_pretty_options {
5856                    self.write_newline();
5857                    self.write_indent();
5858                } else {
5859                    self.write_space();
5860                }
5861                self.write_keyword(key);
5862                // StarRocks/Doris: COMMENT 'value' (no =), others: COMMENT='value'
5863                if key == "COMMENT" && !self.config.schema_comment_with_eq {
5864                    self.write_space();
5865                } else {
5866                    self.write("=");
5867                }
5868                self.write(value);
5869            }
5870        }
5871
5872        // Spark/Databricks: USING PARQUET for temporary tables that don't already have a storage format
5873        if ct.temporary
5874            && matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks))
5875            && ct.as_select.is_none()
5876        {
5877            self.write_space();
5878            self.write_keyword("USING PARQUET");
5879        }
5880
5881        // PostgreSQL INHERITS clause
5882        if !ct.inherits.is_empty() {
5883            self.write_space();
5884            self.write_keyword("INHERITS");
5885            self.write(" (");
5886            for (i, parent) in ct.inherits.iter().enumerate() {
5887                if i > 0 {
5888                    self.write(", ");
5889                }
5890                self.generate_table(parent)?;
5891            }
5892            self.write(")");
5893        }
5894
5895        // CREATE TABLE AS SELECT
5896        if let Some(ref query) = ct.as_select {
5897            self.write_space();
5898            self.write_keyword("AS");
5899            self.write_space();
5900            if ct.as_select_parenthesized {
5901                self.write("(");
5902            }
5903            self.generate_expression(query)?;
5904            if ct.as_select_parenthesized {
5905                self.write(")");
5906            }
5907
5908            // Teradata: WITH DATA / WITH NO DATA
5909            if let Some(with_data) = ct.with_data {
5910                self.write_space();
5911                self.write_keyword("WITH");
5912                if !with_data {
5913                    self.write_space();
5914                    self.write_keyword("NO");
5915                }
5916                self.write_space();
5917                self.write_keyword("DATA");
5918            }
5919
5920            // Teradata: AND STATISTICS / AND NO STATISTICS
5921            if let Some(with_statistics) = ct.with_statistics {
5922                self.write_space();
5923                self.write_keyword("AND");
5924                if !with_statistics {
5925                    self.write_space();
5926                    self.write_keyword("NO");
5927                }
5928                self.write_space();
5929                self.write_keyword("STATISTICS");
5930            }
5931
5932            // Teradata: Index specifications
5933            for index in &ct.teradata_indexes {
5934                self.write_space();
5935                match index.kind {
5936                    TeradataIndexKind::NoPrimary => {
5937                        self.write_keyword("NO PRIMARY INDEX");
5938                    }
5939                    TeradataIndexKind::Primary => {
5940                        self.write_keyword("PRIMARY INDEX");
5941                    }
5942                    TeradataIndexKind::PrimaryAmp => {
5943                        self.write_keyword("PRIMARY AMP INDEX");
5944                    }
5945                    TeradataIndexKind::Unique => {
5946                        self.write_keyword("UNIQUE INDEX");
5947                    }
5948                    TeradataIndexKind::UniquePrimary => {
5949                        self.write_keyword("UNIQUE PRIMARY INDEX");
5950                    }
5951                    TeradataIndexKind::Secondary => {
5952                        self.write_keyword("INDEX");
5953                    }
5954                }
5955                // Output index name if present
5956                if let Some(ref name) = index.name {
5957                    self.write_space();
5958                    self.write(name);
5959                }
5960                // Output columns if present
5961                if !index.columns.is_empty() {
5962                    self.write(" (");
5963                    for (i, col) in index.columns.iter().enumerate() {
5964                        if i > 0 {
5965                            self.write(", ");
5966                        }
5967                        self.write(col);
5968                    }
5969                    self.write(")");
5970                }
5971            }
5972
5973            // Teradata: ON COMMIT behavior for volatile tables
5974            if let Some(ref on_commit) = ct.on_commit {
5975                self.write_space();
5976                self.write_keyword("ON COMMIT");
5977                self.write_space();
5978                match on_commit {
5979                    OnCommit::PreserveRows => self.write_keyword("PRESERVE ROWS"),
5980                    OnCommit::DeleteRows => self.write_keyword("DELETE ROWS"),
5981                }
5982            }
5983
5984            if !post_as_properties.is_empty() {
5985                for prop in post_as_properties {
5986                    self.write_space();
5987                    self.generate_expression(prop)?;
5988                }
5989            }
5990
5991            // Restore Athena Hive context before early return
5992            self.athena_hive_context = saved_athena_hive_context;
5993            return Ok(());
5994        }
5995
5996        // ON COMMIT behavior (for non-CTAS tables)
5997        if let Some(ref on_commit) = ct.on_commit {
5998            self.write_space();
5999            self.write_keyword("ON COMMIT");
6000            self.write_space();
6001            match on_commit {
6002                OnCommit::PreserveRows => self.write_keyword("PRESERVE ROWS"),
6003                OnCommit::DeleteRows => self.write_keyword("DELETE ROWS"),
6004            }
6005        }
6006
6007        // Restore Athena Hive context
6008        self.athena_hive_context = saved_athena_hive_context;
6009
6010        Ok(())
6011    }
6012
6013    /// Generate column definition as an expression (for ROWS FROM alias columns, XMLTABLE/JSON_TABLE)
6014    /// Outputs: "col_name" TYPE [PATH 'xpath'] (not the full CREATE TABLE column definition)
6015    fn generate_column_def_expr(&mut self, col: &ColumnDef) -> Result<()> {
6016        // Output column name
6017        self.generate_identifier(&col.name)?;
6018        // Output data type if known
6019        if !matches!(col.data_type, DataType::Unknown) {
6020            self.write_space();
6021            self.generate_data_type(&col.data_type)?;
6022        }
6023        // Output PATH constraint if present (for XMLTABLE/JSON_TABLE columns)
6024        for constraint in &col.constraints {
6025            if let ColumnConstraint::Path(path_expr) = constraint {
6026                self.write_space();
6027                self.write_keyword("PATH");
6028                self.write_space();
6029                self.generate_expression(path_expr)?;
6030            }
6031        }
6032        Ok(())
6033    }
6034
6035    fn generate_column_def(&mut self, col: &ColumnDef) -> Result<()> {
6036        // Check if this is a TSQL computed column (no data type)
6037        let has_computed_no_type = matches!(&col.data_type, DataType::Custom { name } if name.is_empty())
6038            && col.constraints.iter().any(|c| matches!(c, ColumnConstraint::ComputedColumn(_)));
6039        // Some dialects (notably TSQL/Fabric) do not include an explicit type for computed columns.
6040        let omit_computed_type = !self.config.computed_column_with_type
6041            && col.constraints.iter().any(|c| matches!(c, ColumnConstraint::ComputedColumn(_)));
6042
6043        // Check if this is a partition column spec (no data type, type is Unknown)
6044        // This is used in PostgreSQL PARTITION OF syntax where columns only have constraints
6045        let is_partition_column_spec = matches!(col.data_type, DataType::Unknown);
6046
6047        // Check if this is a DYNAMIC TABLE column (no data type, empty Custom name, no constraints)
6048        // Also check the no_type flag for SQLite columns without types
6049        let has_no_type = col.no_type || (matches!(&col.data_type, DataType::Custom { name } if name.is_empty())
6050            && col.constraints.is_empty());
6051
6052        self.generate_identifier(&col.name)?;
6053
6054        // Check for SERIAL/BIGSERIAL/SMALLSERIAL expansion for Materialize and PostgreSQL
6055        let serial_expansion = if matches!(self.config.dialect, Some(DialectType::Materialize) | Some(DialectType::PostgreSQL)) {
6056            if let DataType::Custom { ref name } = col.data_type {
6057                match name.to_uppercase().as_str() {
6058                    "SERIAL" => Some("INT"),
6059                    "BIGSERIAL" => Some("BIGINT"),
6060                    "SMALLSERIAL" => Some("SMALLINT"),
6061                    _ => None,
6062                }
6063            } else {
6064                None
6065            }
6066        } else {
6067            None
6068        };
6069
6070        if !has_computed_no_type && !omit_computed_type && !is_partition_column_spec && !has_no_type {
6071            self.write_space();
6072            if let Some(int_type) = serial_expansion {
6073                // SERIAL -> INT (+ constraints added below)
6074                self.write_keyword(int_type);
6075            } else if col.unsigned && matches!(self.config.dialect, Some(DialectType::DuckDB)) {
6076                // For DuckDB: convert unsigned integer types to their unsigned equivalents
6077                let unsigned_type = match &col.data_type {
6078                    DataType::Int { .. } => Some("UINTEGER"),
6079                    DataType::BigInt { .. } => Some("UBIGINT"),
6080                    DataType::SmallInt { .. } => Some("USMALLINT"),
6081                    DataType::TinyInt { .. } => Some("UTINYINT"),
6082                    _ => None,
6083                };
6084                if let Some(utype) = unsigned_type {
6085                    self.write_keyword(utype);
6086                } else {
6087                    self.generate_data_type(&col.data_type)?;
6088                }
6089            } else {
6090                self.generate_data_type(&col.data_type)?;
6091            }
6092        }
6093
6094        // MySQL type modifiers (must come right after data type)
6095        // Skip UNSIGNED for DuckDB (already mapped to unsigned type above)
6096        if col.unsigned && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
6097            self.write_space();
6098            self.write_keyword("UNSIGNED");
6099        }
6100        if col.zerofill {
6101            self.write_space();
6102            self.write_keyword("ZEROFILL");
6103        }
6104
6105        // Teradata column attributes (must come right after data type, in specific order)
6106        // ORDER: CHARACTER SET, UPPERCASE, CASESPECIFIC, FORMAT, TITLE, INLINE LENGTH, COMPRESS
6107
6108        if let Some(ref charset) = col.character_set {
6109            self.write_space();
6110            self.write_keyword("CHARACTER SET");
6111            self.write_space();
6112            self.write(charset);
6113        }
6114
6115        if col.uppercase {
6116            self.write_space();
6117            self.write_keyword("UPPERCASE");
6118        }
6119
6120        if let Some(casespecific) = col.casespecific {
6121            self.write_space();
6122            if casespecific {
6123                self.write_keyword("CASESPECIFIC");
6124            } else {
6125                self.write_keyword("NOT CASESPECIFIC");
6126            }
6127        }
6128
6129        if let Some(ref format) = col.format {
6130            self.write_space();
6131            self.write_keyword("FORMAT");
6132            self.write(" '");
6133            self.write(format);
6134            self.write("'");
6135        }
6136
6137        if let Some(ref title) = col.title {
6138            self.write_space();
6139            self.write_keyword("TITLE");
6140            self.write(" '");
6141            self.write(title);
6142            self.write("'");
6143        }
6144
6145        if let Some(length) = col.inline_length {
6146            self.write_space();
6147            self.write_keyword("INLINE LENGTH");
6148            self.write(" ");
6149            self.write(&length.to_string());
6150        }
6151
6152        if let Some(ref compress) = col.compress {
6153            self.write_space();
6154            self.write_keyword("COMPRESS");
6155            if !compress.is_empty() {
6156                // Single string literal: output without parentheses (Teradata syntax)
6157                if compress.len() == 1 {
6158                    if let Expression::Literal(Literal::String(_)) = &compress[0] {
6159                        self.write_space();
6160                        self.generate_expression(&compress[0])?;
6161                    } else {
6162                        self.write(" (");
6163                        self.generate_expression(&compress[0])?;
6164                        self.write(")");
6165                    }
6166                } else {
6167                    self.write(" (");
6168                    for (i, val) in compress.iter().enumerate() {
6169                        if i > 0 {
6170                            self.write(", ");
6171                        }
6172                        self.generate_expression(val)?;
6173                    }
6174                    self.write(")");
6175                }
6176            }
6177        }
6178
6179        // Column constraints - output in original order if constraint_order is populated
6180        // Otherwise fall back to legacy fixed order for backward compatibility
6181        if !col.constraint_order.is_empty() {
6182            // Use constraint_order for original ordering
6183            // Track indices for constraints stored in the constraints Vec
6184            let mut references_idx = 0;
6185            let mut check_idx = 0;
6186            let mut generated_idx = 0;
6187            let mut collate_idx = 0;
6188            let mut comment_idx = 0;
6189            // The preprocessing in dialects/mod.rs now handles the correct ordering of
6190            // NOT NULL relative to IDENTITY for PostgreSQL, so no deferral needed here.
6191            let defer_not_null_after_identity = false;
6192            let mut pending_not_null_after_identity = false;
6193
6194            for constraint_type in &col.constraint_order {
6195                match constraint_type {
6196                    ConstraintType::PrimaryKey => {
6197                        // Materialize doesn't support PRIMARY KEY column constraints
6198                        if col.primary_key && !matches!(self.config.dialect, Some(DialectType::Materialize)) {
6199                            if let Some(ref cname) = col.primary_key_constraint_name {
6200                                self.write_space();
6201                                self.write_keyword("CONSTRAINT");
6202                                self.write_space();
6203                                self.write(cname);
6204                            }
6205                            self.write_space();
6206                            self.write_keyword("PRIMARY KEY");
6207                            if let Some(ref order) = col.primary_key_order {
6208                                self.write_space();
6209                                match order {
6210                                    SortOrder::Asc => self.write_keyword("ASC"),
6211                                    SortOrder::Desc => self.write_keyword("DESC"),
6212                                }
6213                            }
6214                        }
6215                    }
6216                    ConstraintType::Unique => {
6217                        if col.unique {
6218                            if let Some(ref cname) = col.unique_constraint_name {
6219                                self.write_space();
6220                                self.write_keyword("CONSTRAINT");
6221                                self.write_space();
6222                                self.write(cname);
6223                            }
6224                            self.write_space();
6225                            self.write_keyword("UNIQUE");
6226                            // PostgreSQL 15+: NULLS NOT DISTINCT
6227                            if col.unique_nulls_not_distinct {
6228                                self.write(" NULLS NOT DISTINCT");
6229                            }
6230                        }
6231                    }
6232                    ConstraintType::NotNull => {
6233                        if col.nullable == Some(false) {
6234                            if defer_not_null_after_identity {
6235                                pending_not_null_after_identity = true;
6236                                continue;
6237                            }
6238                            if let Some(ref cname) = col.not_null_constraint_name {
6239                                self.write_space();
6240                                self.write_keyword("CONSTRAINT");
6241                                self.write_space();
6242                                self.write(cname);
6243                            }
6244                            self.write_space();
6245                            self.write_keyword("NOT NULL");
6246                        }
6247                    }
6248                    ConstraintType::Null => {
6249                        if col.nullable == Some(true) {
6250                            self.write_space();
6251                            self.write_keyword("NULL");
6252                        }
6253                    }
6254                    ConstraintType::Default => {
6255                        if let Some(ref default) = col.default {
6256                            self.write_space();
6257                            self.write_keyword("DEFAULT");
6258                            self.write_space();
6259                            self.generate_expression(default)?;
6260                        }
6261                    }
6262                    ConstraintType::AutoIncrement => {
6263                        if col.auto_increment {
6264                            // DuckDB doesn't support AUTO_INCREMENT - skip entirely
6265                            if matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB)) {
6266                                // Skip - DuckDB uses sequences or rowid instead
6267                            } else if matches!(self.config.dialect, Some(crate::dialects::DialectType::Materialize)) {
6268                                // Materialize strips AUTO_INCREMENT but adds NOT NULL
6269                                if !matches!(col.nullable, Some(false)) {
6270                                    self.write_space();
6271                                    self.write_keyword("NOT NULL");
6272                                }
6273                            } else {
6274                            self.write_space();
6275                            self.generate_auto_increment_keyword(col)?;
6276                            if pending_not_null_after_identity {
6277                                self.write_space();
6278                                self.write_keyword("NOT NULL");
6279                                pending_not_null_after_identity = false;
6280                            }
6281                        }
6282                        } // close else for DuckDB skip
6283                    }
6284                    ConstraintType::References => {
6285                        // Find next References constraint
6286                        while references_idx < col.constraints.len() {
6287                            if let ColumnConstraint::References(fk_ref) = &col.constraints[references_idx] {
6288                                // CONSTRAINT name if present
6289                                if let Some(ref name) = fk_ref.constraint_name {
6290                                    self.write_space();
6291                                    self.write_keyword("CONSTRAINT");
6292                                    self.write_space();
6293                                    self.write(name);
6294                                }
6295                                self.write_space();
6296                                if fk_ref.has_foreign_key_keywords {
6297                                    self.write_keyword("FOREIGN KEY");
6298                                    self.write_space();
6299                                }
6300                                self.write_keyword("REFERENCES");
6301                                self.write_space();
6302                                self.generate_table(&fk_ref.table)?;
6303                                if !fk_ref.columns.is_empty() {
6304                                    self.write(" (");
6305                                    for (i, c) in fk_ref.columns.iter().enumerate() {
6306                                        if i > 0 {
6307                                            self.write(", ");
6308                                        }
6309                                        self.generate_identifier(c)?;
6310                                    }
6311                                    self.write(")");
6312                                }
6313                                self.generate_referential_actions(fk_ref)?;
6314                                references_idx += 1;
6315                                break;
6316                            }
6317                            references_idx += 1;
6318                        }
6319                    }
6320                    ConstraintType::Check => {
6321                        // Find next Check constraint
6322                        while check_idx < col.constraints.len() {
6323                            if let ColumnConstraint::Check(expr) = &col.constraints[check_idx] {
6324                                // Output CONSTRAINT name if present (only for first CHECK)
6325                                if check_idx == 0 {
6326                                    if let Some(ref cname) = col.check_constraint_name {
6327                                        self.write_space();
6328                                        self.write_keyword("CONSTRAINT");
6329                                        self.write_space();
6330                                        self.write(cname);
6331                                    }
6332                                }
6333                                self.write_space();
6334                                self.write_keyword("CHECK");
6335                                self.write(" (");
6336                                self.generate_expression(expr)?;
6337                                self.write(")");
6338                                check_idx += 1;
6339                                break;
6340                            }
6341                            check_idx += 1;
6342                        }
6343                    }
6344                    ConstraintType::GeneratedAsIdentity => {
6345                        // Find next GeneratedAsIdentity constraint
6346                        while generated_idx < col.constraints.len() {
6347                            if let ColumnConstraint::GeneratedAsIdentity(gen) = &col.constraints[generated_idx] {
6348                                self.write_space();
6349                                // Redshift uses IDENTITY(start, increment) syntax
6350                                if matches!(self.config.dialect, Some(crate::dialects::DialectType::Redshift)) {
6351                                    self.write_keyword("IDENTITY");
6352                                    self.write("(");
6353                                    if let Some(ref start) = gen.start {
6354                                        self.generate_expression(start)?;
6355                                    } else {
6356                                        self.write("0");
6357                                    }
6358                                    self.write(", ");
6359                                    if let Some(ref incr) = gen.increment {
6360                                        self.generate_expression(incr)?;
6361                                    } else {
6362                                        self.write("1");
6363                                    }
6364                                    self.write(")");
6365                                } else {
6366                                    self.write_keyword("GENERATED");
6367                                    if gen.always {
6368                                        self.write_space();
6369                                        self.write_keyword("ALWAYS");
6370                                    } else {
6371                                        self.write_space();
6372                                        self.write_keyword("BY DEFAULT");
6373                                        if gen.on_null {
6374                                            self.write_space();
6375                                            self.write_keyword("ON NULL");
6376                                        }
6377                                    }
6378                                    self.write_space();
6379                                    self.write_keyword("AS IDENTITY");
6380
6381                                    let has_options = gen.start.is_some() || gen.increment.is_some()
6382                                        || gen.minvalue.is_some() || gen.maxvalue.is_some() || gen.cycle.is_some();
6383                                    if has_options {
6384                                        self.write(" (");
6385                                        let mut first = true;
6386                                        if let Some(ref start) = gen.start {
6387                                            if !first { self.write(" "); }
6388                                            first = false;
6389                                            self.write_keyword("START WITH");
6390                                            self.write_space();
6391                                            self.generate_expression(start)?;
6392                                        }
6393                                        if let Some(ref incr) = gen.increment {
6394                                            if !first { self.write(" "); }
6395                                            first = false;
6396                                            self.write_keyword("INCREMENT BY");
6397                                            self.write_space();
6398                                            self.generate_expression(incr)?;
6399                                        }
6400                                        if let Some(ref minv) = gen.minvalue {
6401                                            if !first { self.write(" "); }
6402                                            first = false;
6403                                            self.write_keyword("MINVALUE");
6404                                            self.write_space();
6405                                            self.generate_expression(minv)?;
6406                                        }
6407                                        if let Some(ref maxv) = gen.maxvalue {
6408                                            if !first { self.write(" "); }
6409                                            first = false;
6410                                            self.write_keyword("MAXVALUE");
6411                                            self.write_space();
6412                                            self.generate_expression(maxv)?;
6413                                        }
6414                                        if let Some(cycle) = gen.cycle {
6415                                            if !first { self.write(" "); }
6416                                            if cycle {
6417                                                self.write_keyword("CYCLE");
6418                                            } else {
6419                                                self.write_keyword("NO CYCLE");
6420                                            }
6421                                        }
6422                                        self.write(")");
6423                                    }
6424                                }
6425                                generated_idx += 1;
6426                                break;
6427                            }
6428                            generated_idx += 1;
6429                        }
6430                    }
6431                    ConstraintType::Collate => {
6432                        // Find next Collate constraint
6433                        while collate_idx < col.constraints.len() {
6434                            if let ColumnConstraint::Collate(collation) = &col.constraints[collate_idx] {
6435                                self.write_space();
6436                                self.write_keyword("COLLATE");
6437                                self.write_space();
6438                                self.generate_identifier(collation)?;
6439                                collate_idx += 1;
6440                                break;
6441                            }
6442                            collate_idx += 1;
6443                        }
6444                    }
6445                    ConstraintType::Comment => {
6446                        // Find next Comment constraint
6447                        while comment_idx < col.constraints.len() {
6448                            if let ColumnConstraint::Comment(comment) = &col.constraints[comment_idx] {
6449                                self.write_space();
6450                                self.write_keyword("COMMENT");
6451                                self.write_space();
6452                                self.generate_string_literal(comment)?;
6453                                comment_idx += 1;
6454                                break;
6455                            }
6456                            comment_idx += 1;
6457                        }
6458                    }
6459                    ConstraintType::Tags => {
6460                        // Find next Tags constraint (Snowflake)
6461                        for constraint in &col.constraints {
6462                            if let ColumnConstraint::Tags(tags) = constraint {
6463                                self.write_space();
6464                                self.write_keyword("TAG");
6465                                self.write(" (");
6466                                for (i, expr) in tags.expressions.iter().enumerate() {
6467                                    if i > 0 {
6468                                        self.write(", ");
6469                                    }
6470                                    self.generate_expression(expr)?;
6471                                }
6472                                self.write(")");
6473                                break;
6474                            }
6475                        }
6476                    }
6477                    ConstraintType::ComputedColumn => {
6478                        // Find next ComputedColumn constraint
6479                        for constraint in &col.constraints {
6480                            if let ColumnConstraint::ComputedColumn(cc) = constraint {
6481                                self.write_space();
6482                                self.generate_computed_column_inline(cc)?;
6483                                break;
6484                            }
6485                        }
6486                    }
6487                    ConstraintType::GeneratedAsRow => {
6488                        // Find next GeneratedAsRow constraint
6489                        for constraint in &col.constraints {
6490                            if let ColumnConstraint::GeneratedAsRow(gar) = constraint {
6491                                self.write_space();
6492                                self.generate_generated_as_row_inline(gar)?;
6493                                break;
6494                            }
6495                        }
6496                    }
6497                    ConstraintType::OnUpdate => {
6498                        if let Some(ref expr) = col.on_update {
6499                            self.write_space();
6500                            self.write_keyword("ON UPDATE");
6501                            self.write_space();
6502                            self.generate_expression(expr)?;
6503                        }
6504                    }
6505                    ConstraintType::Encode => {
6506                        if let Some(ref encoding) = col.encoding {
6507                            self.write_space();
6508                            self.write_keyword("ENCODE");
6509                            self.write_space();
6510                            self.write(encoding);
6511                        }
6512                    }
6513                    ConstraintType::Path => {
6514                        // Find next Path constraint
6515                        for constraint in &col.constraints {
6516                            if let ColumnConstraint::Path(path_expr) = constraint {
6517                                self.write_space();
6518                                self.write_keyword("PATH");
6519                                self.write_space();
6520                                self.generate_expression(path_expr)?;
6521                                break;
6522                            }
6523                        }
6524                    }
6525                }
6526            }
6527            if pending_not_null_after_identity {
6528                self.write_space();
6529                self.write_keyword("NOT NULL");
6530            }
6531        } else {
6532            // Legacy fixed order for backward compatibility
6533            if col.primary_key {
6534                self.write_space();
6535                self.write_keyword("PRIMARY KEY");
6536                if let Some(ref order) = col.primary_key_order {
6537                    self.write_space();
6538                    match order {
6539                        SortOrder::Asc => self.write_keyword("ASC"),
6540                        SortOrder::Desc => self.write_keyword("DESC"),
6541                    }
6542                }
6543            }
6544
6545            if col.unique {
6546                self.write_space();
6547                self.write_keyword("UNIQUE");
6548                // PostgreSQL 15+: NULLS NOT DISTINCT
6549                if col.unique_nulls_not_distinct {
6550                    self.write(" NULLS NOT DISTINCT");
6551                }
6552            }
6553
6554            match col.nullable {
6555                Some(false) => {
6556                    self.write_space();
6557                    self.write_keyword("NOT NULL");
6558                }
6559                Some(true) => {
6560                    self.write_space();
6561                    self.write_keyword("NULL");
6562                }
6563                None => {}
6564            }
6565
6566            if let Some(ref default) = col.default {
6567                self.write_space();
6568                self.write_keyword("DEFAULT");
6569                self.write_space();
6570                self.generate_expression(default)?;
6571            }
6572
6573            if col.auto_increment {
6574                self.write_space();
6575                self.generate_auto_increment_keyword(col)?;
6576            }
6577
6578            // Column-level constraints from Vec
6579            for constraint in &col.constraints {
6580                match constraint {
6581                    ColumnConstraint::References(fk_ref) => {
6582                        self.write_space();
6583                        if fk_ref.has_foreign_key_keywords {
6584                            self.write_keyword("FOREIGN KEY");
6585                            self.write_space();
6586                        }
6587                        self.write_keyword("REFERENCES");
6588                        self.write_space();
6589                        self.generate_table(&fk_ref.table)?;
6590                        if !fk_ref.columns.is_empty() {
6591                            self.write(" (");
6592                            for (i, c) in fk_ref.columns.iter().enumerate() {
6593                                if i > 0 {
6594                                    self.write(", ");
6595                                }
6596                                self.generate_identifier(c)?;
6597                            }
6598                            self.write(")");
6599                        }
6600                        self.generate_referential_actions(fk_ref)?;
6601                    }
6602                    ColumnConstraint::Check(expr) => {
6603                        self.write_space();
6604                        self.write_keyword("CHECK");
6605                        self.write(" (");
6606                        self.generate_expression(expr)?;
6607                        self.write(")");
6608                    }
6609                    ColumnConstraint::GeneratedAsIdentity(gen) => {
6610                        self.write_space();
6611                        // Redshift uses IDENTITY(start, increment) syntax
6612                        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Redshift)) {
6613                            self.write_keyword("IDENTITY");
6614                            self.write("(");
6615                            if let Some(ref start) = gen.start {
6616                                self.generate_expression(start)?;
6617                            } else {
6618                                self.write("0");
6619                            }
6620                            self.write(", ");
6621                            if let Some(ref incr) = gen.increment {
6622                                self.generate_expression(incr)?;
6623                            } else {
6624                                self.write("1");
6625                            }
6626                            self.write(")");
6627                        } else {
6628                            self.write_keyword("GENERATED");
6629                            if gen.always {
6630                                self.write_space();
6631                                self.write_keyword("ALWAYS");
6632                            } else {
6633                                self.write_space();
6634                                self.write_keyword("BY DEFAULT");
6635                                if gen.on_null {
6636                                    self.write_space();
6637                                    self.write_keyword("ON NULL");
6638                                }
6639                            }
6640                            self.write_space();
6641                            self.write_keyword("AS IDENTITY");
6642
6643                            let has_options = gen.start.is_some() || gen.increment.is_some()
6644                                || gen.minvalue.is_some() || gen.maxvalue.is_some() || gen.cycle.is_some();
6645                            if has_options {
6646                                self.write(" (");
6647                                let mut first = true;
6648                                if let Some(ref start) = gen.start {
6649                                    if !first { self.write(" "); }
6650                                    first = false;
6651                                    self.write_keyword("START WITH");
6652                                    self.write_space();
6653                                    self.generate_expression(start)?;
6654                                }
6655                                if let Some(ref incr) = gen.increment {
6656                                    if !first { self.write(" "); }
6657                                    first = false;
6658                                    self.write_keyword("INCREMENT BY");
6659                                    self.write_space();
6660                                    self.generate_expression(incr)?;
6661                                }
6662                                if let Some(ref minv) = gen.minvalue {
6663                                    if !first { self.write(" "); }
6664                                    first = false;
6665                                    self.write_keyword("MINVALUE");
6666                                    self.write_space();
6667                                    self.generate_expression(minv)?;
6668                                }
6669                                if let Some(ref maxv) = gen.maxvalue {
6670                                    if !first { self.write(" "); }
6671                                    first = false;
6672                                    self.write_keyword("MAXVALUE");
6673                                    self.write_space();
6674                                    self.generate_expression(maxv)?;
6675                                }
6676                                if let Some(cycle) = gen.cycle {
6677                                    if !first { self.write(" "); }
6678                                    if cycle {
6679                                        self.write_keyword("CYCLE");
6680                                    } else {
6681                                        self.write_keyword("NO CYCLE");
6682                                    }
6683                                }
6684                                self.write(")");
6685                            }
6686                        }
6687                    }
6688                    ColumnConstraint::Collate(collation) => {
6689                        self.write_space();
6690                        self.write_keyword("COLLATE");
6691                        self.write_space();
6692                        self.generate_identifier(collation)?;
6693                    }
6694                    ColumnConstraint::Comment(comment) => {
6695                        self.write_space();
6696                        self.write_keyword("COMMENT");
6697                        self.write_space();
6698                        self.generate_string_literal(comment)?;
6699                    }
6700                    ColumnConstraint::Path(path_expr) => {
6701                        self.write_space();
6702                        self.write_keyword("PATH");
6703                        self.write_space();
6704                        self.generate_expression(path_expr)?;
6705                    }
6706                    _ => {} // Other constraints handled above
6707                }
6708            }
6709
6710            // Redshift: ENCODE encoding_type (legacy path)
6711            if let Some(ref encoding) = col.encoding {
6712                self.write_space();
6713                self.write_keyword("ENCODE");
6714                self.write_space();
6715                self.write(encoding);
6716            }
6717        }
6718
6719        // ClickHouse: CODEC(...)
6720        if let Some(ref codec) = col.codec {
6721            self.write_space();
6722            self.write_keyword("CODEC");
6723            self.write("(");
6724            self.write(codec);
6725            self.write(")");
6726        }
6727
6728        // ClickHouse: EPHEMERAL [expr]
6729        if let Some(ref ephemeral) = col.ephemeral {
6730            self.write_space();
6731            self.write_keyword("EPHEMERAL");
6732            if let Some(ref expr) = ephemeral {
6733                self.write_space();
6734                self.generate_expression(expr)?;
6735            }
6736        }
6737
6738        // ClickHouse: MATERIALIZED expr
6739        if let Some(ref mat_expr) = col.materialized_expr {
6740            self.write_space();
6741            self.write_keyword("MATERIALIZED");
6742            self.write_space();
6743            self.generate_expression(mat_expr)?;
6744        }
6745
6746        // ClickHouse: ALIAS expr
6747        if let Some(ref alias_expr) = col.alias_expr {
6748            self.write_space();
6749            self.write_keyword("ALIAS");
6750            self.write_space();
6751            self.generate_expression(alias_expr)?;
6752        }
6753
6754        // ClickHouse: TTL expr
6755        if let Some(ref ttl_expr) = col.ttl_expr {
6756            self.write_space();
6757            self.write_keyword("TTL");
6758            self.write_space();
6759            self.generate_expression(ttl_expr)?;
6760        }
6761
6762        // TSQL: NOT FOR REPLICATION
6763        if col.not_for_replication && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)) {
6764            self.write_space();
6765            self.write_keyword("NOT FOR REPLICATION");
6766        }
6767
6768        // BigQuery: OPTIONS (key=value, ...) on column - comes after all constraints
6769        if !col.options.is_empty() {
6770            self.write_space();
6771            self.generate_options_clause(&col.options)?;
6772        }
6773
6774        // SQLite: Inline PRIMARY KEY from table constraint
6775        // This comes at the end, after all existing column constraints
6776        if !col.primary_key && self.sqlite_inline_pk_columns.contains(&col.name.name.to_lowercase()) {
6777            self.write_space();
6778            self.write_keyword("PRIMARY KEY");
6779        }
6780
6781        // SERIAL expansion: add GENERATED BY DEFAULT AS IDENTITY NOT NULL for PostgreSQL,
6782        // just NOT NULL for Materialize (which strips GENERATED AS IDENTITY)
6783        if serial_expansion.is_some() {
6784            if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) {
6785                self.write_space();
6786                self.write_keyword("GENERATED BY DEFAULT AS IDENTITY NOT NULL");
6787            } else if matches!(self.config.dialect, Some(DialectType::Materialize)) {
6788                self.write_space();
6789                self.write_keyword("NOT NULL");
6790            }
6791        }
6792
6793        Ok(())
6794    }
6795
6796    fn generate_table_constraint(&mut self, constraint: &TableConstraint) -> Result<()> {
6797        match constraint {
6798            TableConstraint::PrimaryKey { name, columns, include_columns, modifiers, has_constraint_keyword } => {
6799                if let Some(ref n) = name {
6800                    if *has_constraint_keyword {
6801                        self.write_keyword("CONSTRAINT");
6802                        self.write_space();
6803                        self.generate_identifier(n)?;
6804                        self.write_space();
6805                    }
6806                }
6807                self.write_keyword("PRIMARY KEY");
6808                // TSQL CLUSTERED/NONCLUSTERED modifier (before columns)
6809                if let Some(ref clustered) = modifiers.clustered {
6810                    self.write_space();
6811                    self.write_keyword(clustered);
6812                }
6813                // MySQL format: PRIMARY KEY name (cols) when no CONSTRAINT keyword
6814                if let Some(ref n) = name {
6815                    if !*has_constraint_keyword {
6816                        self.write_space();
6817                        self.generate_identifier(n)?;
6818                    }
6819                }
6820                self.write(" (");
6821                for (i, col) in columns.iter().enumerate() {
6822                    if i > 0 {
6823                        self.write(", ");
6824                    }
6825                    self.generate_identifier(col)?;
6826                }
6827                self.write(")");
6828                if !include_columns.is_empty() {
6829                    self.write_space();
6830                    self.write_keyword("INCLUDE");
6831                    self.write(" (");
6832                    for (i, col) in include_columns.iter().enumerate() {
6833                        if i > 0 {
6834                            self.write(", ");
6835                        }
6836                        self.generate_identifier(col)?;
6837                    }
6838                    self.write(")");
6839                }
6840                self.generate_constraint_modifiers(modifiers);
6841            }
6842            TableConstraint::Unique { name, columns, columns_parenthesized, modifiers, has_constraint_keyword, nulls_not_distinct } => {
6843                if let Some(ref n) = name {
6844                    if *has_constraint_keyword {
6845                        self.write_keyword("CONSTRAINT");
6846                        self.write_space();
6847                        self.generate_identifier(n)?;
6848                        self.write_space();
6849                    }
6850                }
6851                self.write_keyword("UNIQUE");
6852                // TSQL CLUSTERED/NONCLUSTERED modifier (before columns)
6853                if let Some(ref clustered) = modifiers.clustered {
6854                    self.write_space();
6855                    self.write_keyword(clustered);
6856                }
6857                // PostgreSQL 15+: NULLS NOT DISTINCT
6858                if *nulls_not_distinct {
6859                    self.write(" NULLS NOT DISTINCT");
6860                }
6861                // MySQL format: UNIQUE name (cols) when no CONSTRAINT keyword
6862                if let Some(ref n) = name {
6863                    if !*has_constraint_keyword {
6864                        self.write_space();
6865                        self.generate_identifier(n)?;
6866                    }
6867                }
6868                if *columns_parenthesized {
6869                    self.write(" (");
6870                    for (i, col) in columns.iter().enumerate() {
6871                        if i > 0 {
6872                            self.write(", ");
6873                        }
6874                        self.generate_identifier(col)?;
6875                    }
6876                    self.write(")");
6877                } else {
6878                    // UNIQUE without parentheses (e.g., UNIQUE idx_name)
6879                    for col in columns.iter() {
6880                        self.write_space();
6881                        self.generate_identifier(col)?;
6882                    }
6883                }
6884                self.generate_constraint_modifiers(modifiers);
6885            }
6886            TableConstraint::ForeignKey { name, columns, references, on_delete, on_update, modifiers } => {
6887                if let Some(ref n) = name {
6888                    self.write_keyword("CONSTRAINT");
6889                    self.write_space();
6890                    self.generate_identifier(n)?;
6891                    self.write_space();
6892                }
6893                self.write_keyword("FOREIGN KEY");
6894                self.write(" (");
6895                for (i, col) in columns.iter().enumerate() {
6896                    if i > 0 {
6897                        self.write(", ");
6898                    }
6899                    self.generate_identifier(col)?;
6900                }
6901                self.write(")");
6902                if let Some(ref refs) = references {
6903                    self.write(" ");
6904                    self.write_keyword("REFERENCES");
6905                    self.write_space();
6906                    self.generate_table(&refs.table)?;
6907                    if !refs.columns.is_empty() {
6908                        if self.config.pretty {
6909                            self.write(" (");
6910                            self.write_newline();
6911                            self.indent_level += 1;
6912                            for (i, col) in refs.columns.iter().enumerate() {
6913                                if i > 0 {
6914                                    self.write(",");
6915                                    self.write_newline();
6916                                }
6917                                self.write_indent();
6918                                self.generate_identifier(col)?;
6919                            }
6920                            self.indent_level -= 1;
6921                            self.write_newline();
6922                            self.write_indent();
6923                            self.write(")");
6924                        } else {
6925                            self.write(" (");
6926                            for (i, col) in refs.columns.iter().enumerate() {
6927                                if i > 0 {
6928                                    self.write(", ");
6929                                }
6930                                self.generate_identifier(col)?;
6931                            }
6932                            self.write(")");
6933                        }
6934                    }
6935                    self.generate_referential_actions(refs)?;
6936                } else {
6937                    // No REFERENCES - output ON DELETE/ON UPDATE directly
6938                    if let Some(ref action) = on_delete {
6939                        self.write_space();
6940                        self.write_keyword("ON DELETE");
6941                        self.write_space();
6942                        self.generate_referential_action(action);
6943                    }
6944                    if let Some(ref action) = on_update {
6945                        self.write_space();
6946                        self.write_keyword("ON UPDATE");
6947                        self.write_space();
6948                        self.generate_referential_action(action);
6949                    }
6950                }
6951                self.generate_constraint_modifiers(modifiers);
6952            }
6953            TableConstraint::Check { name, expression, modifiers } => {
6954                if let Some(ref n) = name {
6955                    self.write_keyword("CONSTRAINT");
6956                    self.write_space();
6957                    self.generate_identifier(n)?;
6958                    self.write_space();
6959                }
6960                self.write_keyword("CHECK");
6961                self.write(" (");
6962                self.generate_expression(expression)?;
6963                self.write(")");
6964                self.generate_constraint_modifiers(modifiers);
6965            }
6966            TableConstraint::Index { name, columns, kind, modifiers, use_key_keyword, expression, index_type, granularity } => {
6967                // ClickHouse-style INDEX: INDEX name expr TYPE type_func GRANULARITY n
6968                if expression.is_some() {
6969                    self.write_keyword("INDEX");
6970                    if let Some(ref n) = name {
6971                        self.write_space();
6972                        self.generate_identifier(n)?;
6973                    }
6974                    if let Some(ref expr) = expression {
6975                        self.write_space();
6976                        self.generate_expression(expr)?;
6977                    }
6978                    if let Some(ref idx_type) = index_type {
6979                        self.write_space();
6980                        self.write_keyword("TYPE");
6981                        self.write_space();
6982                        self.generate_expression(idx_type)?;
6983                    }
6984                    if let Some(ref gran) = granularity {
6985                        self.write_space();
6986                        self.write_keyword("GRANULARITY");
6987                        self.write_space();
6988                        self.generate_expression(gran)?;
6989                    }
6990                } else {
6991                    // Standard INDEX syntax
6992                    // Determine the index keyword to use
6993                    // MySQL normalizes KEY to INDEX
6994                    use crate::dialects::DialectType;
6995                    let index_keyword = if *use_key_keyword && !matches!(self.config.dialect, Some(DialectType::MySQL)) {
6996                        "KEY"
6997                    } else {
6998                        "INDEX"
6999                    };
7000
7001                    // Output kind (UNIQUE, FULLTEXT, SPATIAL) if present
7002                    if let Some(ref k) = kind {
7003                        self.write_keyword(k);
7004                        // For UNIQUE, don't add INDEX/KEY keyword
7005                        if k != "UNIQUE" {
7006                            self.write_space();
7007                            self.write_keyword(index_keyword);
7008                        }
7009                    } else {
7010                        self.write_keyword(index_keyword);
7011                    }
7012
7013                    // Output USING before name if using_before_columns is true and there's no name
7014                    if modifiers.using_before_columns && name.is_none() {
7015                        if let Some(ref using) = modifiers.using {
7016                            self.write_space();
7017                            self.write_keyword("USING");
7018                            self.write_space();
7019                            self.write_keyword(using);
7020                        }
7021                    }
7022
7023                    // Output index name if present
7024                    if let Some(ref n) = name {
7025                        self.write_space();
7026                        self.generate_identifier(n)?;
7027                    }
7028
7029                    // Output USING after name but before columns if using_before_columns and there's a name
7030                    if modifiers.using_before_columns && name.is_some() {
7031                        if let Some(ref using) = modifiers.using {
7032                            self.write_space();
7033                            self.write_keyword("USING");
7034                            self.write_space();
7035                            self.write_keyword(using);
7036                        }
7037                    }
7038
7039                    // Output columns
7040                    self.write(" (");
7041                    for (i, col) in columns.iter().enumerate() {
7042                        if i > 0 {
7043                            self.write(", ");
7044                        }
7045                        self.generate_identifier(col)?;
7046                    }
7047                    self.write(")");
7048
7049                    // Output USING after columns if not using_before_columns
7050                    if !modifiers.using_before_columns {
7051                        if let Some(ref using) = modifiers.using {
7052                            self.write_space();
7053                            self.write_keyword("USING");
7054                            self.write_space();
7055                            self.write_keyword(using);
7056                        }
7057                    }
7058
7059                    // Output other constraint modifiers (but skip USING since we already handled it)
7060                    self.generate_constraint_modifiers_without_using(modifiers);
7061                }
7062            }
7063            TableConstraint::Projection { name, expression } => {
7064                // ClickHouse: PROJECTION name (SELECT ...)
7065                self.write_keyword("PROJECTION");
7066                self.write_space();
7067                self.generate_identifier(name)?;
7068                self.write(" (");
7069                self.generate_expression(expression)?;
7070                self.write(")");
7071            }
7072            TableConstraint::Like { source, options } => {
7073                self.write_keyword("LIKE");
7074                self.write_space();
7075                self.generate_table(source)?;
7076                for (action, prop) in options {
7077                    self.write_space();
7078                    match action {
7079                        LikeOptionAction::Including => self.write_keyword("INCLUDING"),
7080                        LikeOptionAction::Excluding => self.write_keyword("EXCLUDING"),
7081                    }
7082                    self.write_space();
7083                    self.write_keyword(prop);
7084                }
7085            }
7086            TableConstraint::PeriodForSystemTime { start_col, end_col } => {
7087                self.write_keyword("PERIOD FOR SYSTEM_TIME");
7088                self.write(" (");
7089                self.generate_identifier(start_col)?;
7090                self.write(", ");
7091                self.generate_identifier(end_col)?;
7092                self.write(")");
7093            }
7094            TableConstraint::Exclude { name, using, elements, include_columns, where_clause, with_params, using_index_tablespace, modifiers: _ } => {
7095                if let Some(ref n) = name {
7096                    self.write_keyword("CONSTRAINT");
7097                    self.write_space();
7098                    self.generate_identifier(n)?;
7099                    self.write_space();
7100                }
7101                self.write_keyword("EXCLUDE");
7102                if let Some(ref method) = using {
7103                    self.write_space();
7104                    self.write_keyword("USING");
7105                    self.write_space();
7106                    self.write(method);
7107                    self.write("(");
7108                } else {
7109                    self.write(" (");
7110                }
7111                for (i, elem) in elements.iter().enumerate() {
7112                    if i > 0 {
7113                        self.write(", ");
7114                    }
7115                    self.write(&elem.expression);
7116                    self.write_space();
7117                    self.write_keyword("WITH");
7118                    self.write_space();
7119                    self.write(&elem.operator);
7120                }
7121                self.write(")");
7122                if !include_columns.is_empty() {
7123                    self.write_space();
7124                    self.write_keyword("INCLUDE");
7125                    self.write(" (");
7126                    for (i, col) in include_columns.iter().enumerate() {
7127                        if i > 0 {
7128                            self.write(", ");
7129                        }
7130                        self.generate_identifier(col)?;
7131                    }
7132                    self.write(")");
7133                }
7134                if !with_params.is_empty() {
7135                    self.write_space();
7136                    self.write_keyword("WITH");
7137                    self.write(" (");
7138                    for (i, (key, val)) in with_params.iter().enumerate() {
7139                        if i > 0 {
7140                            self.write(", ");
7141                        }
7142                        self.write(key);
7143                        self.write("=");
7144                        self.write(val);
7145                    }
7146                    self.write(")");
7147                }
7148                if let Some(ref tablespace) = using_index_tablespace {
7149                    self.write_space();
7150                    self.write_keyword("USING INDEX TABLESPACE");
7151                    self.write_space();
7152                    self.write(tablespace);
7153                }
7154                if let Some(ref where_expr) = where_clause {
7155                    self.write_space();
7156                    self.write_keyword("WHERE");
7157                    self.write(" (");
7158                    self.generate_expression(where_expr)?;
7159                    self.write(")");
7160                }
7161            }
7162            TableConstraint::Tags(tags) => {
7163                self.write_keyword("TAG");
7164                self.write(" (");
7165                for (i, expr) in tags.expressions.iter().enumerate() {
7166                    if i > 0 {
7167                        self.write(", ");
7168                    }
7169                    self.generate_expression(expr)?;
7170                }
7171                self.write(")");
7172            }
7173            TableConstraint::InitiallyDeferred { deferred } => {
7174                self.write_keyword("INITIALLY");
7175                self.write_space();
7176                if *deferred {
7177                    self.write_keyword("DEFERRED");
7178                } else {
7179                    self.write_keyword("IMMEDIATE");
7180                }
7181            }
7182        }
7183        Ok(())
7184    }
7185
7186    fn generate_constraint_modifiers(&mut self, modifiers: &ConstraintModifiers) {
7187        // Output USING BTREE/HASH (MySQL) - comes first
7188        if let Some(using) = &modifiers.using {
7189            self.write_space();
7190            self.write_keyword("USING");
7191            self.write_space();
7192            self.write_keyword(using);
7193        }
7194        // Output ENFORCED/NOT ENFORCED
7195        if let Some(enforced) = modifiers.enforced {
7196            self.write_space();
7197            if enforced {
7198                self.write_keyword("ENFORCED");
7199            } else {
7200                self.write_keyword("NOT ENFORCED");
7201            }
7202        }
7203        // Output DEFERRABLE/NOT DEFERRABLE
7204        if let Some(deferrable) = modifiers.deferrable {
7205            self.write_space();
7206            if deferrable {
7207                self.write_keyword("DEFERRABLE");
7208            } else {
7209                self.write_keyword("NOT DEFERRABLE");
7210            }
7211        }
7212        // Output INITIALLY DEFERRED/INITIALLY IMMEDIATE
7213        if let Some(initially_deferred) = modifiers.initially_deferred {
7214            self.write_space();
7215            if initially_deferred {
7216                self.write_keyword("INITIALLY DEFERRED");
7217            } else {
7218                self.write_keyword("INITIALLY IMMEDIATE");
7219            }
7220        }
7221        // Output NORELY
7222        if modifiers.norely {
7223            self.write_space();
7224            self.write_keyword("NORELY");
7225        }
7226        // Output RELY
7227        if modifiers.rely {
7228            self.write_space();
7229            self.write_keyword("RELY");
7230        }
7231        // Output NOT VALID (PostgreSQL)
7232        if modifiers.not_valid {
7233            self.write_space();
7234            self.write_keyword("NOT VALID");
7235        }
7236        // Output ON CONFLICT (SQLite)
7237        if let Some(on_conflict) = &modifiers.on_conflict {
7238            self.write_space();
7239            self.write_keyword("ON CONFLICT");
7240            self.write_space();
7241            self.write_keyword(on_conflict);
7242        }
7243        // Output TSQL WITH options (PAD_INDEX=ON, STATISTICS_NORECOMPUTE=OFF, ...)
7244        if !modifiers.with_options.is_empty() {
7245            self.write_space();
7246            self.write_keyword("WITH");
7247            self.write(" (");
7248            for (i, (key, value)) in modifiers.with_options.iter().enumerate() {
7249                if i > 0 {
7250                    self.write(", ");
7251                }
7252                self.write(key);
7253                self.write("=");
7254                self.write(value);
7255            }
7256            self.write(")");
7257        }
7258        // Output TSQL ON filegroup
7259        if let Some(ref fg) = modifiers.on_filegroup {
7260            self.write_space();
7261            self.write_keyword("ON");
7262            self.write_space();
7263            let _ = self.generate_identifier(fg);
7264        }
7265    }
7266
7267    /// Generate constraint modifiers without USING (for Index constraints where USING is handled separately)
7268    fn generate_constraint_modifiers_without_using(&mut self, modifiers: &ConstraintModifiers) {
7269        // Output ENFORCED/NOT ENFORCED
7270        if let Some(enforced) = modifiers.enforced {
7271            self.write_space();
7272            if enforced {
7273                self.write_keyword("ENFORCED");
7274            } else {
7275                self.write_keyword("NOT ENFORCED");
7276            }
7277        }
7278        // Output DEFERRABLE/NOT DEFERRABLE
7279        if let Some(deferrable) = modifiers.deferrable {
7280            self.write_space();
7281            if deferrable {
7282                self.write_keyword("DEFERRABLE");
7283            } else {
7284                self.write_keyword("NOT DEFERRABLE");
7285            }
7286        }
7287        // Output INITIALLY DEFERRED/INITIALLY IMMEDIATE
7288        if let Some(initially_deferred) = modifiers.initially_deferred {
7289            self.write_space();
7290            if initially_deferred {
7291                self.write_keyword("INITIALLY DEFERRED");
7292            } else {
7293                self.write_keyword("INITIALLY IMMEDIATE");
7294            }
7295        }
7296        // Output NORELY
7297        if modifiers.norely {
7298            self.write_space();
7299            self.write_keyword("NORELY");
7300        }
7301        // Output RELY
7302        if modifiers.rely {
7303            self.write_space();
7304            self.write_keyword("RELY");
7305        }
7306        // Output NOT VALID (PostgreSQL)
7307        if modifiers.not_valid {
7308            self.write_space();
7309            self.write_keyword("NOT VALID");
7310        }
7311        // Output ON CONFLICT (SQLite)
7312        if let Some(on_conflict) = &modifiers.on_conflict {
7313            self.write_space();
7314            self.write_keyword("ON CONFLICT");
7315            self.write_space();
7316            self.write_keyword(on_conflict);
7317        }
7318        // Output MySQL index-specific modifiers
7319        self.generate_index_specific_modifiers(modifiers);
7320    }
7321
7322    /// Generate MySQL index-specific modifiers (COMMENT, VISIBLE, ENGINE_ATTRIBUTE, WITH PARSER)
7323    fn generate_index_specific_modifiers(&mut self, modifiers: &ConstraintModifiers) {
7324        if let Some(ref comment) = modifiers.comment {
7325            self.write_space();
7326            self.write_keyword("COMMENT");
7327            self.write(" '");
7328            self.write(comment);
7329            self.write("'");
7330        }
7331        if let Some(visible) = modifiers.visible {
7332            self.write_space();
7333            if visible {
7334                self.write_keyword("VISIBLE");
7335            } else {
7336                self.write_keyword("INVISIBLE");
7337            }
7338        }
7339        if let Some(ref attr) = modifiers.engine_attribute {
7340            self.write_space();
7341            self.write_keyword("ENGINE_ATTRIBUTE");
7342            self.write(" = '");
7343            self.write(attr);
7344            self.write("'");
7345        }
7346        if let Some(ref parser) = modifiers.with_parser {
7347            self.write_space();
7348            self.write_keyword("WITH PARSER");
7349            self.write_space();
7350            self.write(parser);
7351        }
7352    }
7353
7354    fn generate_referential_actions(&mut self, fk_ref: &ForeignKeyRef) -> Result<()> {
7355        // MATCH clause before ON DELETE/ON UPDATE (default position, e.g. PostgreSQL)
7356        if !fk_ref.match_after_actions {
7357            if let Some(ref match_type) = fk_ref.match_type {
7358                self.write_space();
7359                self.write_keyword("MATCH");
7360                self.write_space();
7361                match match_type {
7362                    MatchType::Full => self.write_keyword("FULL"),
7363                    MatchType::Partial => self.write_keyword("PARTIAL"),
7364                    MatchType::Simple => self.write_keyword("SIMPLE"),
7365                }
7366            }
7367        }
7368
7369        // Output ON UPDATE and ON DELETE in the original order
7370        if fk_ref.on_update_first {
7371            if let Some(ref action) = fk_ref.on_update {
7372                self.write_space();
7373                self.write_keyword("ON UPDATE");
7374                self.write_space();
7375                self.generate_referential_action(action);
7376            }
7377            if let Some(ref action) = fk_ref.on_delete {
7378                self.write_space();
7379                self.write_keyword("ON DELETE");
7380                self.write_space();
7381                self.generate_referential_action(action);
7382            }
7383        } else {
7384            if let Some(ref action) = fk_ref.on_delete {
7385                self.write_space();
7386                self.write_keyword("ON DELETE");
7387                self.write_space();
7388                self.generate_referential_action(action);
7389            }
7390            if let Some(ref action) = fk_ref.on_update {
7391                self.write_space();
7392                self.write_keyword("ON UPDATE");
7393                self.write_space();
7394                self.generate_referential_action(action);
7395            }
7396        }
7397
7398        // MATCH clause after ON DELETE/ON UPDATE (when original SQL had it after)
7399        if fk_ref.match_after_actions {
7400            if let Some(ref match_type) = fk_ref.match_type {
7401                self.write_space();
7402                self.write_keyword("MATCH");
7403                self.write_space();
7404                match match_type {
7405                    MatchType::Full => self.write_keyword("FULL"),
7406                    MatchType::Partial => self.write_keyword("PARTIAL"),
7407                    MatchType::Simple => self.write_keyword("SIMPLE"),
7408                }
7409            }
7410        }
7411
7412        // DEFERRABLE / NOT DEFERRABLE
7413        if let Some(deferrable) = fk_ref.deferrable {
7414            self.write_space();
7415            if deferrable {
7416                self.write_keyword("DEFERRABLE");
7417            } else {
7418                self.write_keyword("NOT DEFERRABLE");
7419            }
7420        }
7421
7422        Ok(())
7423    }
7424
7425    fn generate_referential_action(&mut self, action: &ReferentialAction) {
7426        match action {
7427            ReferentialAction::Cascade => self.write_keyword("CASCADE"),
7428            ReferentialAction::SetNull => self.write_keyword("SET NULL"),
7429            ReferentialAction::SetDefault => self.write_keyword("SET DEFAULT"),
7430            ReferentialAction::Restrict => self.write_keyword("RESTRICT"),
7431            ReferentialAction::NoAction => self.write_keyword("NO ACTION"),
7432        }
7433    }
7434
7435    fn generate_drop_table(&mut self, dt: &DropTable) -> Result<()> {
7436        // Athena: DROP TABLE uses Hive engine (backticks)
7437        let saved_athena_hive_context = self.athena_hive_context;
7438        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
7439            self.athena_hive_context = true;
7440        }
7441
7442        self.write_keyword("DROP TABLE");
7443
7444        if dt.if_exists {
7445            self.write_space();
7446            self.write_keyword("IF EXISTS");
7447        }
7448
7449        self.write_space();
7450        for (i, table) in dt.names.iter().enumerate() {
7451            if i > 0 {
7452                self.write(", ");
7453            }
7454            self.generate_table(table)?;
7455        }
7456
7457        if dt.cascade_constraints {
7458            self.write_space();
7459            self.write_keyword("CASCADE CONSTRAINTS");
7460        } else if dt.cascade {
7461            self.write_space();
7462            self.write_keyword("CASCADE");
7463        }
7464
7465        if dt.purge {
7466            self.write_space();
7467            self.write_keyword("PURGE");
7468        }
7469
7470        // Restore Athena Hive context
7471        self.athena_hive_context = saved_athena_hive_context;
7472
7473        Ok(())
7474    }
7475
7476    fn generate_alter_table(&mut self, at: &AlterTable) -> Result<()> {
7477        // Athena: ALTER TABLE uses Hive engine (backticks)
7478        let saved_athena_hive_context = self.athena_hive_context;
7479        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
7480            self.athena_hive_context = true;
7481        }
7482
7483        self.write_keyword("ALTER TABLE");
7484        if at.if_exists {
7485            self.write_space();
7486            self.write_keyword("IF EXISTS");
7487        }
7488        self.write_space();
7489        self.generate_table(&at.name)?;
7490
7491        // ClickHouse: ON CLUSTER clause
7492        if let Some(ref on_cluster) = at.on_cluster {
7493            self.write_space();
7494            self.generate_on_cluster(on_cluster)?;
7495        }
7496
7497        // Hive: PARTITION(key=value, ...) clause
7498        if let Some(ref partition) = at.partition {
7499            self.write_space();
7500            self.write_keyword("PARTITION");
7501            self.write("(");
7502            for (i, (key, value)) in partition.iter().enumerate() {
7503                if i > 0 {
7504                    self.write(", ");
7505                }
7506                self.generate_identifier(key)?;
7507                self.write(" = ");
7508                self.generate_expression(value)?;
7509            }
7510            self.write(")");
7511        }
7512
7513        // TSQL: WITH CHECK / WITH NOCHECK modifier
7514        if let Some(ref with_check) = at.with_check {
7515            self.write_space();
7516            self.write_keyword(with_check);
7517        }
7518
7519        if self.config.pretty {
7520            // In pretty mode, format actions with newlines and indentation
7521            self.write_newline();
7522            self.indent_level += 1;
7523            for (i, action) in at.actions.iter().enumerate() {
7524                // Check if this is a continuation of previous ADD COLUMN or ADD CONSTRAINT
7525                let is_continuation = i > 0 && matches!(
7526                    (&at.actions[i - 1], action),
7527                    (AlterTableAction::AddColumn { .. }, AlterTableAction::AddColumn { .. })
7528                    | (AlterTableAction::AddConstraint(_), AlterTableAction::AddConstraint(_))
7529                );
7530                if i > 0 {
7531                    self.write(",");
7532                    self.write_newline();
7533                }
7534                self.write_indent();
7535                self.generate_alter_action_with_continuation(action, is_continuation)?;
7536            }
7537            self.indent_level -= 1;
7538        } else {
7539            for (i, action) in at.actions.iter().enumerate() {
7540                // Check if this is a continuation of previous ADD COLUMN or ADD CONSTRAINT
7541                let is_continuation = i > 0 && matches!(
7542                    (&at.actions[i - 1], action),
7543                    (AlterTableAction::AddColumn { .. }, AlterTableAction::AddColumn { .. })
7544                    | (AlterTableAction::AddConstraint(_), AlterTableAction::AddConstraint(_))
7545                );
7546                if i > 0 {
7547                    self.write(",");
7548                }
7549                self.write_space();
7550                self.generate_alter_action_with_continuation(action, is_continuation)?;
7551            }
7552        }
7553
7554        // MySQL ALTER TABLE trailing options
7555        if let Some(ref algorithm) = at.algorithm {
7556            self.write(", ");
7557            self.write_keyword("ALGORITHM");
7558            self.write("=");
7559            self.write_keyword(algorithm);
7560        }
7561        if let Some(ref lock) = at.lock {
7562            self.write(", ");
7563            self.write_keyword("LOCK");
7564            self.write("=");
7565            self.write_keyword(lock);
7566        }
7567
7568        // Restore Athena Hive context
7569        self.athena_hive_context = saved_athena_hive_context;
7570
7571        Ok(())
7572    }
7573
7574    fn generate_alter_action_with_continuation(&mut self, action: &AlterTableAction, is_continuation: bool) -> Result<()> {
7575        match action {
7576            AlterTableAction::AddColumn { column, if_not_exists, position } => {
7577                use crate::dialects::DialectType;
7578                // For Snowflake: consecutive ADD COLUMN actions are combined with commas
7579                // e.g., "ADD col1, col2" instead of "ADD col1, ADD col2"
7580                // For other dialects, repeat ADD COLUMN for each
7581                let is_snowflake = matches!(self.config.dialect, Some(DialectType::Snowflake));
7582                let is_tsql_like = matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric));
7583                // Athena uses "ADD COLUMNS (col_def)" instead of "ADD COLUMN col_def"
7584                let is_athena = matches!(self.config.dialect, Some(DialectType::Athena));
7585
7586                if is_continuation && (is_snowflake || is_tsql_like) {
7587                    // Don't write ADD keyword for continuation in Snowflake/TSQL
7588                } else if is_snowflake {
7589                    self.write_keyword("ADD");
7590                    self.write_space();
7591                } else if is_athena {
7592                    // Athena uses ADD COLUMNS (col_def) syntax
7593                    self.write_keyword("ADD COLUMNS");
7594                    self.write(" (");
7595                } else if self.config.alter_table_include_column_keyword {
7596                    self.write_keyword("ADD COLUMN");
7597                    self.write_space();
7598                } else {
7599                    // Dialects like Oracle and TSQL don't use COLUMN keyword
7600                    self.write_keyword("ADD");
7601                    self.write_space();
7602                }
7603
7604                if *if_not_exists {
7605                    self.write_keyword("IF NOT EXISTS");
7606                    self.write_space();
7607                }
7608                self.generate_column_def(column)?;
7609
7610                // Close parenthesis for Athena
7611                if is_athena {
7612                    self.write(")");
7613                }
7614
7615                // Column position (FIRST or AFTER)
7616                if let Some(pos) = position {
7617                    self.write_space();
7618                    match pos {
7619                        ColumnPosition::First => self.write_keyword("FIRST"),
7620                        ColumnPosition::After(col_name) => {
7621                            self.write_keyword("AFTER");
7622                            self.write_space();
7623                            self.generate_identifier(col_name)?;
7624                        }
7625                    }
7626                }
7627            }
7628            AlterTableAction::DropColumn { name, if_exists, cascade } => {
7629                self.write_keyword("DROP COLUMN");
7630                if *if_exists {
7631                    self.write_space();
7632                    self.write_keyword("IF EXISTS");
7633                }
7634                self.write_space();
7635                self.generate_identifier(name)?;
7636                if *cascade {
7637                    self.write_space();
7638                    self.write_keyword("CASCADE");
7639                }
7640            }
7641            AlterTableAction::DropColumns { names } => {
7642                self.write_keyword("DROP COLUMNS");
7643                self.write(" (");
7644                for (i, name) in names.iter().enumerate() {
7645                    if i > 0 {
7646                        self.write(", ");
7647                    }
7648                    self.generate_identifier(name)?;
7649                }
7650                self.write(")");
7651            }
7652            AlterTableAction::RenameColumn { old_name, new_name, if_exists } => {
7653                self.write_keyword("RENAME COLUMN");
7654                if *if_exists {
7655                    self.write_space();
7656                    self.write_keyword("IF EXISTS");
7657                }
7658                self.write_space();
7659                self.generate_identifier(old_name)?;
7660                self.write_space();
7661                self.write_keyword("TO");
7662                self.write_space();
7663                self.generate_identifier(new_name)?;
7664            }
7665            AlterTableAction::AlterColumn { name, action, use_modify_keyword } => {
7666                use crate::dialects::DialectType;
7667                // MySQL uses MODIFY COLUMN for type changes (SetDataType)
7668                // but ALTER COLUMN for SET DEFAULT, DROP DEFAULT, etc.
7669                let use_modify = *use_modify_keyword || (
7670                    matches!(self.config.dialect, Some(DialectType::MySQL)) &&
7671                    matches!(action, AlterColumnAction::SetDataType { .. })
7672                );
7673                if use_modify {
7674                    self.write_keyword("MODIFY COLUMN");
7675                    self.write_space();
7676                    self.generate_identifier(name)?;
7677                    // For MODIFY COLUMN, output the type directly
7678                    if let AlterColumnAction::SetDataType { data_type, using: _, collate } = action {
7679                        self.write_space();
7680                        self.generate_data_type(data_type)?;
7681                        // Output COLLATE clause if present
7682                        if let Some(collate_name) = collate {
7683                            self.write_space();
7684                            self.write_keyword("COLLATE");
7685                            self.write_space();
7686                            // Output as single-quoted string
7687                            self.write(&format!("'{}'", collate_name));
7688                        }
7689                    } else {
7690                        self.write_space();
7691                        self.generate_alter_column_action(action)?;
7692                    }
7693                } else if matches!(self.config.dialect, Some(DialectType::Hive))
7694                    && matches!(action, AlterColumnAction::SetDataType { .. })
7695                {
7696                    // Hive uses CHANGE COLUMN col_name col_name NEW_TYPE
7697                    self.write_keyword("CHANGE COLUMN");
7698                    self.write_space();
7699                    self.generate_identifier(name)?;
7700                    self.write_space();
7701                    self.generate_identifier(name)?;
7702                    if let AlterColumnAction::SetDataType { data_type, .. } = action {
7703                        self.write_space();
7704                        self.generate_data_type(data_type)?;
7705                    }
7706                } else {
7707                    self.write_keyword("ALTER COLUMN");
7708                    self.write_space();
7709                    self.generate_identifier(name)?;
7710                    self.write_space();
7711                    self.generate_alter_column_action(action)?;
7712                }
7713            }
7714            AlterTableAction::RenameTable(new_name) => {
7715                // MySQL-like dialects (MySQL, Doris, StarRocks) use RENAME without TO
7716                let mysql_like = matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::Doris) | Some(DialectType::StarRocks) | Some(DialectType::SingleStore));
7717                if mysql_like {
7718                    self.write_keyword("RENAME");
7719                } else {
7720                    self.write_keyword("RENAME TO");
7721                }
7722                self.write_space();
7723                // Doris, DuckDB, BigQuery, PostgreSQL strip schema/catalog from target table
7724                let rename_table_with_db = !matches!(self.config.dialect, Some(DialectType::Doris) | Some(DialectType::DuckDB) | Some(DialectType::BigQuery) | Some(DialectType::PostgreSQL));
7725                if !rename_table_with_db {
7726                    let mut stripped = new_name.clone();
7727                    stripped.schema = None;
7728                    stripped.catalog = None;
7729                    self.generate_table(&stripped)?;
7730                } else {
7731                    self.generate_table(new_name)?;
7732                }
7733            }
7734            AlterTableAction::AddConstraint(constraint) => {
7735                // For consecutive ADD CONSTRAINT actions (is_continuation=true), skip ADD keyword
7736                // to produce: ADD CONSTRAINT c1 ..., CONSTRAINT c2 ...
7737                if !is_continuation {
7738                    self.write_keyword("ADD");
7739                    self.write_space();
7740                }
7741                self.generate_table_constraint(constraint)?;
7742            }
7743            AlterTableAction::DropConstraint { name, if_exists } => {
7744                self.write_keyword("DROP CONSTRAINT");
7745                if *if_exists {
7746                    self.write_space();
7747                    self.write_keyword("IF EXISTS");
7748                }
7749                self.write_space();
7750                self.generate_identifier(name)?;
7751            }
7752            AlterTableAction::DropForeignKey { name } => {
7753                self.write_keyword("DROP FOREIGN KEY");
7754                self.write_space();
7755                self.generate_identifier(name)?;
7756            }
7757            AlterTableAction::DropPartition { partitions, if_exists } => {
7758                self.write_keyword("DROP");
7759                if *if_exists {
7760                    self.write_space();
7761                    self.write_keyword("IF EXISTS");
7762                }
7763                for (i, partition) in partitions.iter().enumerate() {
7764                    if i > 0 {
7765                        self.write(",");
7766                    }
7767                    self.write_space();
7768                    self.write_keyword("PARTITION");
7769                    // Check for special ClickHouse partition formats
7770                    if partition.len() == 1 && partition[0].0.name == "__expr__" {
7771                        // ClickHouse: PARTITION <expression>
7772                        self.write_space();
7773                        self.generate_expression(&partition[0].1)?;
7774                    } else if partition.len() == 1 && partition[0].0.name == "ALL" {
7775                        // ClickHouse: PARTITION ALL
7776                        self.write_space();
7777                        self.write_keyword("ALL");
7778                    } else if partition.len() == 1 && partition[0].0.name == "ID" {
7779                        // ClickHouse: PARTITION ID 'string'
7780                        self.write_space();
7781                        self.write_keyword("ID");
7782                        self.write_space();
7783                        self.generate_expression(&partition[0].1)?;
7784                    } else {
7785                        // Standard SQL: PARTITION(key=value, ...)
7786                        self.write("(");
7787                        for (j, (key, value)) in partition.iter().enumerate() {
7788                            if j > 0 {
7789                                self.write(", ");
7790                            }
7791                            self.generate_identifier(key)?;
7792                            self.write(" = ");
7793                            self.generate_expression(value)?;
7794                        }
7795                        self.write(")");
7796                    }
7797                }
7798            }
7799            AlterTableAction::Delete { where_clause } => {
7800                self.write_keyword("DELETE");
7801                self.write_space();
7802                self.write_keyword("WHERE");
7803                self.write_space();
7804                self.generate_expression(where_clause)?;
7805            }
7806            AlterTableAction::SwapWith(target) => {
7807                self.write_keyword("SWAP WITH");
7808                self.write_space();
7809                self.generate_table(target)?;
7810            }
7811            AlterTableAction::SetProperty { properties } => {
7812                use crate::dialects::DialectType;
7813                self.write_keyword("SET");
7814                // Trino/Presto use SET PROPERTIES syntax with spaces around =
7815                let is_trino_presto = matches!(self.config.dialect, Some(DialectType::Trino) | Some(DialectType::Presto));
7816                if is_trino_presto {
7817                    self.write_space();
7818                    self.write_keyword("PROPERTIES");
7819                }
7820                let eq = if is_trino_presto { " = " } else { "=" };
7821                for (i, (key, value)) in properties.iter().enumerate() {
7822                    if i > 0 {
7823                        self.write(",");
7824                    }
7825                    self.write_space();
7826                    // Handle quoted property names for Trino
7827                    if key.contains(' ') {
7828                        self.generate_string_literal(key)?;
7829                    } else {
7830                        self.write(key);
7831                    }
7832                    self.write(eq);
7833                    self.generate_expression(value)?;
7834                }
7835            }
7836            AlterTableAction::UnsetProperty { properties } => {
7837                self.write_keyword("UNSET");
7838                for (i, name) in properties.iter().enumerate() {
7839                    if i > 0 {
7840                        self.write(",");
7841                    }
7842                    self.write_space();
7843                    self.write(name);
7844                }
7845            }
7846            AlterTableAction::ClusterBy { expressions } => {
7847                self.write_keyword("CLUSTER BY");
7848                self.write(" (");
7849                for (i, expr) in expressions.iter().enumerate() {
7850                    if i > 0 {
7851                        self.write(", ");
7852                    }
7853                    self.generate_expression(expr)?;
7854                }
7855                self.write(")");
7856            }
7857            AlterTableAction::SetTag { expressions } => {
7858                self.write_keyword("SET TAG");
7859                for (i, (key, value)) in expressions.iter().enumerate() {
7860                    if i > 0 {
7861                        self.write(",");
7862                    }
7863                    self.write_space();
7864                    self.write(key);
7865                    self.write(" = ");
7866                    self.generate_expression(value)?;
7867                }
7868            }
7869            AlterTableAction::UnsetTag { names } => {
7870                self.write_keyword("UNSET TAG");
7871                for (i, name) in names.iter().enumerate() {
7872                    if i > 0 {
7873                        self.write(",");
7874                    }
7875                    self.write_space();
7876                    self.write(name);
7877                }
7878            }
7879            AlterTableAction::SetOptions { expressions } => {
7880                self.write_keyword("SET");
7881                self.write(" (");
7882                for (i, expr) in expressions.iter().enumerate() {
7883                    if i > 0 {
7884                        self.write(", ");
7885                    }
7886                    self.generate_expression(expr)?;
7887                }
7888                self.write(")");
7889            }
7890            AlterTableAction::AlterIndex { name, visible } => {
7891                self.write_keyword("ALTER INDEX");
7892                self.write_space();
7893                self.generate_identifier(name)?;
7894                self.write_space();
7895                if *visible {
7896                    self.write_keyword("VISIBLE");
7897                } else {
7898                    self.write_keyword("INVISIBLE");
7899                }
7900            }
7901            AlterTableAction::SetAttribute { attribute } => {
7902                self.write_keyword("SET");
7903                self.write_space();
7904                self.write_keyword(attribute);
7905            }
7906            AlterTableAction::SetStageFileFormat { options } => {
7907                self.write_keyword("SET");
7908                self.write_space();
7909                self.write_keyword("STAGE_FILE_FORMAT");
7910                self.write(" = (");
7911                if let Some(opts) = options {
7912                    self.generate_space_separated_properties(opts)?;
7913                }
7914                self.write(")");
7915            }
7916            AlterTableAction::SetStageCopyOptions { options } => {
7917                self.write_keyword("SET");
7918                self.write_space();
7919                self.write_keyword("STAGE_COPY_OPTIONS");
7920                self.write(" = (");
7921                if let Some(opts) = options {
7922                    self.generate_space_separated_properties(opts)?;
7923                }
7924                self.write(")");
7925            }
7926            AlterTableAction::AddColumns { columns, cascade } => {
7927                // Oracle uses ADD (...) without COLUMNS keyword
7928                // Hive/Spark uses ADD COLUMNS (...)
7929                let is_oracle = matches!(self.config.dialect, Some(DialectType::Oracle));
7930                if is_oracle {
7931                    self.write_keyword("ADD");
7932                } else {
7933                    self.write_keyword("ADD COLUMNS");
7934                }
7935                self.write(" (");
7936                for (i, col) in columns.iter().enumerate() {
7937                    if i > 0 {
7938                        self.write(", ");
7939                    }
7940                    self.generate_column_def(col)?;
7941                }
7942                self.write(")");
7943                if *cascade {
7944                    self.write_space();
7945                    self.write_keyword("CASCADE");
7946                }
7947            }
7948            AlterTableAction::ChangeColumn { old_name, new_name, data_type, comment, cascade } => {
7949                use crate::dialects::DialectType;
7950                let is_spark = matches!(self.config.dialect,
7951                    Some(DialectType::Spark) | Some(DialectType::Databricks));
7952                let is_rename = old_name.name != new_name.name;
7953
7954                if is_spark {
7955                    if is_rename {
7956                        // Spark: RENAME COLUMN old TO new
7957                        self.write_keyword("RENAME COLUMN");
7958                        self.write_space();
7959                        self.generate_identifier(old_name)?;
7960                        self.write_space();
7961                        self.write_keyword("TO");
7962                        self.write_space();
7963                        self.generate_identifier(new_name)?;
7964                    } else if comment.is_some() {
7965                        // Spark: ALTER COLUMN old COMMENT 'comment'
7966                        self.write_keyword("ALTER COLUMN");
7967                        self.write_space();
7968                        self.generate_identifier(old_name)?;
7969                        self.write_space();
7970                        self.write_keyword("COMMENT");
7971                        self.write_space();
7972                        self.write("'");
7973                        self.write(comment.as_ref().unwrap());
7974                        self.write("'");
7975                    } else if data_type.is_some() {
7976                        // Spark: ALTER COLUMN old TYPE data_type
7977                        self.write_keyword("ALTER COLUMN");
7978                        self.write_space();
7979                        self.generate_identifier(old_name)?;
7980                        self.write_space();
7981                        self.write_keyword("TYPE");
7982                        self.write_space();
7983                        self.generate_data_type(data_type.as_ref().unwrap())?;
7984                    } else {
7985                        // Fallback to CHANGE COLUMN
7986                        self.write_keyword("CHANGE COLUMN");
7987                        self.write_space();
7988                        self.generate_identifier(old_name)?;
7989                        self.write_space();
7990                        self.generate_identifier(new_name)?;
7991                    }
7992                } else {
7993                    // Hive/MySQL/default: CHANGE [COLUMN] old new [type] [COMMENT '...'] [CASCADE]
7994                    if data_type.is_some() {
7995                        self.write_keyword("CHANGE COLUMN");
7996                    } else {
7997                        self.write_keyword("CHANGE");
7998                    }
7999                    self.write_space();
8000                    self.generate_identifier(old_name)?;
8001                    self.write_space();
8002                    self.generate_identifier(new_name)?;
8003                    if let Some(ref dt) = data_type {
8004                        self.write_space();
8005                        self.generate_data_type(dt)?;
8006                    }
8007                    if let Some(ref c) = comment {
8008                        self.write_space();
8009                        self.write_keyword("COMMENT");
8010                        self.write_space();
8011                        self.write("'");
8012                        self.write(c);
8013                        self.write("'");
8014                    }
8015                    if *cascade {
8016                        self.write_space();
8017                        self.write_keyword("CASCADE");
8018                    }
8019                }
8020            }
8021            AlterTableAction::AddPartition { partition, if_not_exists, location } => {
8022                self.write_keyword("ADD");
8023                self.write_space();
8024                if *if_not_exists {
8025                    self.write_keyword("IF NOT EXISTS");
8026                    self.write_space();
8027                }
8028                self.generate_expression(partition)?;
8029                if let Some(ref loc) = location {
8030                    self.write_space();
8031                    self.write_keyword("LOCATION");
8032                    self.write_space();
8033                    self.generate_expression(loc)?;
8034                }
8035            }
8036            AlterTableAction::AlterSortKey { this, expressions, compound } => {
8037                // Redshift: ALTER [COMPOUND] SORTKEY AUTO|NONE|(col1, col2)
8038                self.write_keyword("ALTER");
8039                if *compound {
8040                    self.write_space();
8041                    self.write_keyword("COMPOUND");
8042                }
8043                self.write_space();
8044                self.write_keyword("SORTKEY");
8045                self.write_space();
8046                if let Some(style) = this {
8047                    self.write_keyword(style);
8048                } else if !expressions.is_empty() {
8049                    self.write("(");
8050                    for (i, expr) in expressions.iter().enumerate() {
8051                        if i > 0 {
8052                            self.write(", ");
8053                        }
8054                        self.generate_expression(expr)?;
8055                    }
8056                    self.write(")");
8057                }
8058            }
8059            AlterTableAction::AlterDistStyle { style, distkey } => {
8060                // Redshift: ALTER DISTSTYLE ALL|EVEN|AUTO|KEY [DISTKEY col]
8061                self.write_keyword("ALTER");
8062                self.write_space();
8063                self.write_keyword("DISTSTYLE");
8064                self.write_space();
8065                self.write_keyword(style);
8066                if let Some(col) = distkey {
8067                    self.write_space();
8068                    self.write_keyword("DISTKEY");
8069                    self.write_space();
8070                    self.generate_identifier(col)?;
8071                }
8072            }
8073            AlterTableAction::SetTableProperties { properties } => {
8074                // Redshift: SET TABLE PROPERTIES ('a' = '5', 'b' = 'c')
8075                self.write_keyword("SET TABLE PROPERTIES");
8076                self.write(" (");
8077                for (i, (key, value)) in properties.iter().enumerate() {
8078                    if i > 0 {
8079                        self.write(", ");
8080                    }
8081                    self.generate_expression(key)?;
8082                    self.write(" = ");
8083                    self.generate_expression(value)?;
8084                }
8085                self.write(")");
8086            }
8087            AlterTableAction::SetLocation { location } => {
8088                // Redshift: SET LOCATION 's3://bucket/folder/'
8089                self.write_keyword("SET LOCATION");
8090                self.write_space();
8091                self.write("'");
8092                self.write(location);
8093                self.write("'");
8094            }
8095            AlterTableAction::SetFileFormat { format } => {
8096                // Redshift: SET FILE FORMAT AVRO
8097                self.write_keyword("SET FILE FORMAT");
8098                self.write_space();
8099                self.write_keyword(format);
8100            }
8101            AlterTableAction::ReplacePartition { partition, source } => {
8102                // ClickHouse: REPLACE PARTITION expr FROM source
8103                self.write_keyword("REPLACE PARTITION");
8104                self.write_space();
8105                self.generate_expression(partition)?;
8106                if let Some(src) = source {
8107                    self.write_space();
8108                    self.write_keyword("FROM");
8109                    self.write_space();
8110                    self.generate_expression(src)?;
8111                }
8112            }
8113        }
8114        Ok(())
8115    }
8116
8117    fn generate_alter_column_action(&mut self, action: &AlterColumnAction) -> Result<()> {
8118        match action {
8119            AlterColumnAction::SetDataType { data_type, using, collate } => {
8120                use crate::dialects::DialectType;
8121                // Dialect-specific type change syntax:
8122                // - TSQL/Fabric/Hive: no prefix (ALTER COLUMN col datatype)
8123                // - Redshift/Spark: TYPE (ALTER COLUMN col TYPE datatype)
8124                // - Default: SET DATA TYPE (ALTER COLUMN col SET DATA TYPE datatype)
8125                let is_no_prefix = matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric) | Some(DialectType::Hive));
8126                let is_type_only = matches!(self.config.dialect, Some(DialectType::Redshift) | Some(DialectType::Spark) | Some(DialectType::Databricks));
8127                if is_type_only {
8128                    self.write_keyword("TYPE");
8129                    self.write_space();
8130                } else if !is_no_prefix {
8131                    self.write_keyword("SET DATA TYPE");
8132                    self.write_space();
8133                }
8134                self.generate_data_type(data_type)?;
8135                if let Some(ref collation) = collate {
8136                    self.write_space();
8137                    self.write_keyword("COLLATE");
8138                    self.write_space();
8139                    self.write(collation);
8140                }
8141                if let Some(ref using_expr) = using {
8142                    self.write_space();
8143                    self.write_keyword("USING");
8144                    self.write_space();
8145                    self.generate_expression(using_expr)?;
8146                }
8147            }
8148            AlterColumnAction::SetDefault(expr) => {
8149                self.write_keyword("SET DEFAULT");
8150                self.write_space();
8151                self.generate_expression(expr)?;
8152            }
8153            AlterColumnAction::DropDefault => {
8154                self.write_keyword("DROP DEFAULT");
8155            }
8156            AlterColumnAction::SetNotNull => {
8157                self.write_keyword("SET NOT NULL");
8158            }
8159            AlterColumnAction::DropNotNull => {
8160                self.write_keyword("DROP NOT NULL");
8161            }
8162            AlterColumnAction::Comment(comment) => {
8163                self.write_keyword("COMMENT");
8164                self.write_space();
8165                self.generate_string_literal(comment)?;
8166            }
8167            AlterColumnAction::SetVisible => {
8168                self.write_keyword("SET VISIBLE");
8169            }
8170            AlterColumnAction::SetInvisible => {
8171                self.write_keyword("SET INVISIBLE");
8172            }
8173        }
8174        Ok(())
8175    }
8176
8177    fn generate_create_index(&mut self, ci: &CreateIndex) -> Result<()> {
8178        self.write_keyword("CREATE");
8179
8180        if ci.unique {
8181            self.write_space();
8182            self.write_keyword("UNIQUE");
8183        }
8184
8185        // TSQL CLUSTERED/NONCLUSTERED modifier
8186        if let Some(ref clustered) = ci.clustered {
8187            self.write_space();
8188            self.write_keyword(clustered);
8189        }
8190
8191        self.write_space();
8192        self.write_keyword("INDEX");
8193
8194        // PostgreSQL CONCURRENTLY modifier
8195        if ci.concurrently {
8196            self.write_space();
8197            self.write_keyword("CONCURRENTLY");
8198        }
8199
8200        if ci.if_not_exists {
8201            self.write_space();
8202            self.write_keyword("IF NOT EXISTS");
8203        }
8204
8205        // Index name is optional in PostgreSQL when IF NOT EXISTS is specified
8206        if !ci.name.name.is_empty() {
8207            self.write_space();
8208            self.generate_identifier(&ci.name)?;
8209        }
8210        self.write_space();
8211        self.write_keyword("ON");
8212        self.write_space();
8213        self.generate_table(&ci.table)?;
8214
8215        // Column list (optional for COLUMNSTORE indexes)
8216        // Standard SQL convention: ON t(a) without space before paren
8217        if !ci.columns.is_empty() || ci.using.is_some() {
8218            let space_before_paren = false;
8219
8220            if let Some(ref using) = ci.using {
8221                self.write_space();
8222                self.write_keyword("USING");
8223                self.write_space();
8224                self.write(using);
8225                if space_before_paren {
8226                    self.write(" (");
8227                } else {
8228                    self.write("(");
8229                }
8230            } else {
8231                if space_before_paren {
8232                    self.write(" (");
8233                } else {
8234                    self.write("(");
8235                }
8236            }
8237            for (i, col) in ci.columns.iter().enumerate() {
8238                if i > 0 {
8239                    self.write(", ");
8240                }
8241                self.generate_identifier(&col.column)?;
8242                if let Some(ref opclass) = col.opclass {
8243                    self.write_space();
8244                    self.write(opclass);
8245                }
8246                if col.desc {
8247                    self.write_space();
8248                    self.write_keyword("DESC");
8249                } else if col.asc {
8250                    self.write_space();
8251                    self.write_keyword("ASC");
8252                }
8253                if let Some(nulls_first) = col.nulls_first {
8254                    self.write_space();
8255                    self.write_keyword("NULLS");
8256                    self.write_space();
8257                    self.write_keyword(if nulls_first { "FIRST" } else { "LAST" });
8258                }
8259            }
8260            self.write(")");
8261        }
8262
8263        // PostgreSQL INCLUDE (col1, col2) clause
8264        if !ci.include_columns.is_empty() {
8265            self.write_space();
8266            self.write_keyword("INCLUDE");
8267            self.write(" (");
8268            for (i, col) in ci.include_columns.iter().enumerate() {
8269                if i > 0 {
8270                    self.write(", ");
8271                }
8272                self.generate_identifier(col)?;
8273            }
8274            self.write(")");
8275        }
8276
8277        // TSQL: WITH (option=value, ...) clause
8278        if !ci.with_options.is_empty() {
8279            self.write_space();
8280            self.write_keyword("WITH");
8281            self.write(" (");
8282            for (i, (key, value)) in ci.with_options.iter().enumerate() {
8283                if i > 0 {
8284                    self.write(", ");
8285                }
8286                self.write(key);
8287                self.write("=");
8288                self.write(value);
8289            }
8290            self.write(")");
8291        }
8292
8293        // PostgreSQL WHERE clause for partial indexes
8294        if let Some(ref where_clause) = ci.where_clause {
8295            self.write_space();
8296            self.write_keyword("WHERE");
8297            self.write_space();
8298            self.generate_expression(where_clause)?;
8299        }
8300
8301        // TSQL: ON filegroup or partition scheme clause
8302        if let Some(ref on_fg) = ci.on_filegroup {
8303            self.write_space();
8304            self.write_keyword("ON");
8305            self.write_space();
8306            self.write(on_fg);
8307        }
8308
8309        Ok(())
8310    }
8311
8312    fn generate_drop_index(&mut self, di: &DropIndex) -> Result<()> {
8313        self.write_keyword("DROP INDEX");
8314
8315        if di.concurrently {
8316            self.write_space();
8317            self.write_keyword("CONCURRENTLY");
8318        }
8319
8320        if di.if_exists {
8321            self.write_space();
8322            self.write_keyword("IF EXISTS");
8323        }
8324
8325        self.write_space();
8326        self.generate_identifier(&di.name)?;
8327
8328        if let Some(ref table) = di.table {
8329            self.write_space();
8330            self.write_keyword("ON");
8331            self.write_space();
8332            self.generate_table(table)?;
8333        }
8334
8335        Ok(())
8336    }
8337
8338    fn generate_create_view(&mut self, cv: &CreateView) -> Result<()> {
8339        self.write_keyword("CREATE");
8340
8341        // MySQL: ALGORITHM=...
8342        if let Some(ref algorithm) = cv.algorithm {
8343            self.write_space();
8344            self.write_keyword("ALGORITHM");
8345            self.write("=");
8346            self.write_keyword(algorithm);
8347        }
8348
8349        // MySQL: DEFINER=...
8350        if let Some(ref definer) = cv.definer {
8351            self.write_space();
8352            self.write_keyword("DEFINER");
8353            self.write("=");
8354            self.write(definer);
8355        }
8356
8357        // MySQL: SQL SECURITY DEFINER/INVOKER (before VIEW keyword)
8358        if cv.security_sql_style {
8359            if let Some(ref security) = cv.security {
8360                self.write_space();
8361                self.write_keyword("SQL SECURITY");
8362                self.write_space();
8363                match security {
8364                    FunctionSecurity::Definer => self.write_keyword("DEFINER"),
8365                    FunctionSecurity::Invoker => self.write_keyword("INVOKER"),
8366                    FunctionSecurity::None => self.write_keyword("NONE"),
8367                }
8368            }
8369        }
8370
8371        if cv.or_replace {
8372            self.write_space();
8373            self.write_keyword("OR REPLACE");
8374        }
8375
8376        if cv.temporary {
8377            self.write_space();
8378            self.write_keyword("TEMPORARY");
8379        }
8380
8381        if cv.materialized {
8382            self.write_space();
8383            self.write_keyword("MATERIALIZED");
8384        }
8385
8386        // Snowflake: SECURE VIEW
8387        if cv.secure {
8388            self.write_space();
8389            self.write_keyword("SECURE");
8390        }
8391
8392        self.write_space();
8393        self.write_keyword("VIEW");
8394
8395        if cv.if_not_exists {
8396            self.write_space();
8397            self.write_keyword("IF NOT EXISTS");
8398        }
8399
8400        self.write_space();
8401        self.generate_table(&cv.name)?;
8402
8403        // ClickHouse: ON CLUSTER clause
8404        if let Some(ref on_cluster) = cv.on_cluster {
8405            self.write_space();
8406            self.generate_on_cluster(on_cluster)?;
8407        }
8408
8409        // ClickHouse: TO destination_table
8410        if let Some(ref to_table) = cv.to_table {
8411            self.write_space();
8412            self.write_keyword("TO");
8413            self.write_space();
8414            self.generate_table(to_table)?;
8415        }
8416
8417        // For regular VIEW: columns come before COPY GRANTS
8418        // For MATERIALIZED VIEW: COPY GRANTS comes before columns
8419        if !cv.materialized {
8420            // Regular VIEW: columns first
8421            if !cv.columns.is_empty() {
8422                self.write(" (");
8423                for (i, col) in cv.columns.iter().enumerate() {
8424                    if i > 0 {
8425                        self.write(", ");
8426                    }
8427                    self.generate_identifier(&col.name)?;
8428                    // BigQuery: OPTIONS (key=value, ...) on view column
8429                    if !col.options.is_empty() {
8430                        self.write_space();
8431                        self.generate_options_clause(&col.options)?;
8432                    }
8433                    if let Some(ref comment) = col.comment {
8434                        self.write_space();
8435                        self.write_keyword("COMMENT");
8436                        self.write_space();
8437                        self.generate_string_literal(comment)?;
8438                    }
8439                }
8440                self.write(")");
8441            }
8442
8443            // Presto/Trino/StarRocks: SECURITY DEFINER/INVOKER/NONE (after columns)
8444            if !cv.security_sql_style {
8445                if let Some(ref security) = cv.security {
8446                    self.write_space();
8447                    self.write_keyword("SECURITY");
8448                    self.write_space();
8449                    match security {
8450                        FunctionSecurity::Definer => self.write_keyword("DEFINER"),
8451                        FunctionSecurity::Invoker => self.write_keyword("INVOKER"),
8452                        FunctionSecurity::None => self.write_keyword("NONE"),
8453                    }
8454                }
8455            }
8456
8457            // Snowflake: COPY GRANTS
8458            if cv.copy_grants {
8459                self.write_space();
8460                self.write_keyword("COPY GRANTS");
8461            }
8462        } else {
8463            // MATERIALIZED VIEW: COPY GRANTS first
8464            if cv.copy_grants {
8465                self.write_space();
8466                self.write_keyword("COPY GRANTS");
8467            }
8468
8469            // Doris: If we have a schema (typed columns), generate that instead
8470            if let Some(ref schema) = cv.schema {
8471                self.write(" (");
8472                for (i, expr) in schema.expressions.iter().enumerate() {
8473                    if i > 0 {
8474                        self.write(", ");
8475                    }
8476                    self.generate_expression(expr)?;
8477                }
8478                self.write(")");
8479            } else if !cv.columns.is_empty() {
8480                // Then columns (simple column names without types)
8481                self.write(" (");
8482                for (i, col) in cv.columns.iter().enumerate() {
8483                    if i > 0 {
8484                        self.write(", ");
8485                    }
8486                    self.generate_identifier(&col.name)?;
8487                    // BigQuery: OPTIONS (key=value, ...) on view column
8488                    if !col.options.is_empty() {
8489                        self.write_space();
8490                        self.generate_options_clause(&col.options)?;
8491                    }
8492                    if let Some(ref comment) = col.comment {
8493                        self.write_space();
8494                        self.write_keyword("COMMENT");
8495                        self.write_space();
8496                        self.generate_string_literal(comment)?;
8497                    }
8498                }
8499                self.write(")");
8500            }
8501
8502            // Doris: KEY (columns) for materialized views
8503            if let Some(ref unique_key) = cv.unique_key {
8504                self.write_space();
8505                self.write_keyword("KEY");
8506                self.write(" (");
8507                for (i, expr) in unique_key.expressions.iter().enumerate() {
8508                    if i > 0 {
8509                        self.write(", ");
8510                    }
8511                    self.generate_expression(expr)?;
8512                }
8513                self.write(")");
8514            }
8515        }
8516
8517        // Snowflake: COMMENT = 'text'
8518        if let Some(ref comment) = cv.comment {
8519            self.write_space();
8520            self.write_keyword("COMMENT");
8521            self.write("=");
8522            self.generate_string_literal(comment)?;
8523        }
8524
8525        // Snowflake: TAG (name='value', ...)
8526        if !cv.tags.is_empty() {
8527            self.write_space();
8528            self.write_keyword("TAG");
8529            self.write(" (");
8530            for (i, (name, value)) in cv.tags.iter().enumerate() {
8531                if i > 0 {
8532                    self.write(", ");
8533                }
8534                self.write(name);
8535                self.write("='");
8536                self.write(value);
8537                self.write("'");
8538            }
8539            self.write(")");
8540        }
8541
8542        // BigQuery: OPTIONS (key=value, ...)
8543        if !cv.options.is_empty() {
8544            self.write_space();
8545            self.generate_options_clause(&cv.options)?;
8546        }
8547
8548        // Doris: BUILD IMMEDIATE/DEFERRED for materialized views
8549        if let Some(ref build) = cv.build {
8550            self.write_space();
8551            self.write_keyword("BUILD");
8552            self.write_space();
8553            self.write_keyword(build);
8554        }
8555
8556        // Doris: REFRESH clause for materialized views
8557        if let Some(ref refresh) = cv.refresh {
8558            self.write_space();
8559            self.generate_refresh_trigger_property(refresh)?;
8560        }
8561
8562        // Redshift: AUTO REFRESH YES|NO for materialized views
8563        if let Some(auto_refresh) = cv.auto_refresh {
8564            self.write_space();
8565            self.write_keyword("AUTO REFRESH");
8566            self.write_space();
8567            if auto_refresh {
8568                self.write_keyword("YES");
8569            } else {
8570                self.write_keyword("NO");
8571            }
8572        }
8573
8574        // ClickHouse: Table properties (ENGINE, ORDER BY, SAMPLE, SETTINGS, TTL, etc.)
8575        for prop in &cv.table_properties {
8576            self.write_space();
8577            self.generate_expression(prop)?;
8578        }
8579
8580        // Only output AS clause if there's a real query (not just NULL placeholder)
8581        if !matches!(&cv.query, Expression::Null(_)) {
8582            self.write_space();
8583            self.write_keyword("AS");
8584            self.write_space();
8585
8586            // Teradata: LOCKING clause (between AS and query)
8587            if let Some(ref mode) = cv.locking_mode {
8588                self.write_keyword("LOCKING");
8589                self.write_space();
8590                self.write_keyword(mode);
8591                if let Some(ref access) = cv.locking_access {
8592                    self.write_space();
8593                    self.write_keyword("FOR");
8594                    self.write_space();
8595                    self.write_keyword(access);
8596                }
8597                self.write_space();
8598            }
8599
8600            if cv.query_parenthesized {
8601                self.write("(");
8602            }
8603            self.generate_expression(&cv.query)?;
8604            if cv.query_parenthesized {
8605                self.write(")");
8606            }
8607        }
8608
8609        // Redshift: WITH NO SCHEMA BINDING (after query)
8610        if cv.no_schema_binding {
8611            self.write_space();
8612            self.write_keyword("WITH NO SCHEMA BINDING");
8613        }
8614
8615        Ok(())
8616    }
8617
8618    fn generate_drop_view(&mut self, dv: &DropView) -> Result<()> {
8619        self.write_keyword("DROP");
8620
8621        if dv.materialized {
8622            self.write_space();
8623            self.write_keyword("MATERIALIZED");
8624        }
8625
8626        self.write_space();
8627        self.write_keyword("VIEW");
8628
8629        if dv.if_exists {
8630            self.write_space();
8631            self.write_keyword("IF EXISTS");
8632        }
8633
8634        self.write_space();
8635        self.generate_table(&dv.name)?;
8636
8637        Ok(())
8638    }
8639
8640    fn generate_truncate(&mut self, tr: &Truncate) -> Result<()> {
8641        match tr.target {
8642            TruncateTarget::Database => self.write_keyword("TRUNCATE DATABASE"),
8643            TruncateTarget::Table => self.write_keyword("TRUNCATE TABLE"),
8644        }
8645        self.write_space();
8646        self.generate_table(&tr.table)?;
8647
8648        // ClickHouse: ON CLUSTER clause
8649        if let Some(ref on_cluster) = tr.on_cluster {
8650            self.write_space();
8651            self.generate_on_cluster(on_cluster)?;
8652        }
8653
8654        // Check if first table has a * (multi-table with star)
8655        if !tr.extra_tables.is_empty() {
8656            // Check if the first entry matches the main table (star case)
8657            let skip_first = if let Some(first) = tr.extra_tables.first() {
8658                first.table.name == tr.table.name && first.star
8659            } else {
8660                false
8661            };
8662
8663            // PostgreSQL normalizes away the * suffix (it's the default behavior)
8664            let strip_star = matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL) | Some(crate::dialects::DialectType::Redshift));
8665            if skip_first && !strip_star {
8666                self.write("*");
8667            }
8668
8669            // Generate additional tables
8670            for (i, entry) in tr.extra_tables.iter().enumerate() {
8671                if i == 0 && skip_first {
8672                    continue; // Already handled the star for first table
8673                }
8674                self.write(", ");
8675                self.generate_table(&entry.table)?;
8676                if entry.star && !strip_star {
8677                    self.write("*");
8678                }
8679            }
8680        }
8681
8682        // RESTART/CONTINUE IDENTITY
8683        if let Some(identity) = &tr.identity {
8684            self.write_space();
8685            match identity {
8686                TruncateIdentity::Restart => self.write_keyword("RESTART IDENTITY"),
8687                TruncateIdentity::Continue => self.write_keyword("CONTINUE IDENTITY"),
8688            }
8689        }
8690
8691        if tr.cascade {
8692            self.write_space();
8693            self.write_keyword("CASCADE");
8694        }
8695
8696        if tr.restrict {
8697            self.write_space();
8698            self.write_keyword("RESTRICT");
8699        }
8700
8701        // Output Hive PARTITION clause
8702        if let Some(ref partition) = tr.partition {
8703            self.write_space();
8704            self.generate_expression(partition)?;
8705        }
8706
8707        Ok(())
8708    }
8709
8710    fn generate_use(&mut self, u: &Use) -> Result<()> {
8711        // Teradata uses "DATABASE <name>" instead of "USE <name>"
8712        if matches!(self.config.dialect, Some(DialectType::Teradata)) {
8713            self.write_keyword("DATABASE");
8714            self.write_space();
8715            self.generate_identifier(&u.this)?;
8716            return Ok(());
8717        }
8718
8719        self.write_keyword("USE");
8720
8721        if let Some(kind) = &u.kind {
8722            self.write_space();
8723            match kind {
8724                UseKind::Database => self.write_keyword("DATABASE"),
8725                UseKind::Schema => self.write_keyword("SCHEMA"),
8726                UseKind::Role => self.write_keyword("ROLE"),
8727                UseKind::Warehouse => self.write_keyword("WAREHOUSE"),
8728                UseKind::Catalog => self.write_keyword("CATALOG"),
8729                UseKind::SecondaryRoles => self.write_keyword("SECONDARY ROLES"),
8730            }
8731        }
8732
8733        self.write_space();
8734        // For SECONDARY ROLES, write the value as-is (ALL, NONE, or role names)
8735        // without quoting, since these are keywords not identifiers
8736        if matches!(&u.kind, Some(UseKind::SecondaryRoles)) {
8737            self.write(&u.this.name);
8738        } else {
8739            self.generate_identifier(&u.this)?;
8740        }
8741        Ok(())
8742    }
8743
8744    fn generate_cache(&mut self, c: &Cache) -> Result<()> {
8745        self.write_keyword("CACHE");
8746        if c.lazy {
8747            self.write_space();
8748            self.write_keyword("LAZY");
8749        }
8750        self.write_space();
8751        self.write_keyword("TABLE");
8752        self.write_space();
8753        self.generate_identifier(&c.table)?;
8754
8755        // OPTIONS clause
8756        if !c.options.is_empty() {
8757            self.write_space();
8758            self.write_keyword("OPTIONS");
8759            self.write("(");
8760            for (i, (key, value)) in c.options.iter().enumerate() {
8761                if i > 0 {
8762                    self.write(", ");
8763                }
8764                self.generate_expression(key)?;
8765                self.write(" = ");
8766                self.generate_expression(value)?;
8767            }
8768            self.write(")");
8769        }
8770
8771        // AS query
8772        if let Some(query) = &c.query {
8773            self.write_space();
8774            self.write_keyword("AS");
8775            self.write_space();
8776            self.generate_expression(query)?;
8777        }
8778
8779        Ok(())
8780    }
8781
8782    fn generate_uncache(&mut self, u: &Uncache) -> Result<()> {
8783        self.write_keyword("UNCACHE TABLE");
8784        if u.if_exists {
8785            self.write_space();
8786            self.write_keyword("IF EXISTS");
8787        }
8788        self.write_space();
8789        self.generate_identifier(&u.table)?;
8790        Ok(())
8791    }
8792
8793    fn generate_load_data(&mut self, l: &LoadData) -> Result<()> {
8794        self.write_keyword("LOAD DATA");
8795        if l.local {
8796            self.write_space();
8797            self.write_keyword("LOCAL");
8798        }
8799        self.write_space();
8800        self.write_keyword("INPATH");
8801        self.write_space();
8802        self.write("'");
8803        self.write(&l.inpath);
8804        self.write("'");
8805
8806        if l.overwrite {
8807            self.write_space();
8808            self.write_keyword("OVERWRITE");
8809        }
8810
8811        self.write_space();
8812        self.write_keyword("INTO TABLE");
8813        self.write_space();
8814        self.generate_expression(&l.table)?;
8815
8816        // PARTITION clause
8817        if !l.partition.is_empty() {
8818            self.write_space();
8819            self.write_keyword("PARTITION");
8820            self.write("(");
8821            for (i, (col, val)) in l.partition.iter().enumerate() {
8822                if i > 0 {
8823                    self.write(", ");
8824                }
8825                self.generate_identifier(col)?;
8826                self.write(" = ");
8827                self.generate_expression(val)?;
8828            }
8829            self.write(")");
8830        }
8831
8832        // INPUTFORMAT clause
8833        if let Some(fmt) = &l.input_format {
8834            self.write_space();
8835            self.write_keyword("INPUTFORMAT");
8836            self.write_space();
8837            self.write("'");
8838            self.write(fmt);
8839            self.write("'");
8840        }
8841
8842        // SERDE clause
8843        if let Some(serde) = &l.serde {
8844            self.write_space();
8845            self.write_keyword("SERDE");
8846            self.write_space();
8847            self.write("'");
8848            self.write(serde);
8849            self.write("'");
8850        }
8851
8852        Ok(())
8853    }
8854
8855    fn generate_pragma(&mut self, p: &Pragma) -> Result<()> {
8856        self.write_keyword("PRAGMA");
8857        self.write_space();
8858
8859        // Schema prefix if present
8860        if let Some(schema) = &p.schema {
8861            self.generate_identifier(schema)?;
8862            self.write(".");
8863        }
8864
8865        // Pragma name
8866        self.generate_identifier(&p.name)?;
8867
8868        // Value assignment or function call
8869        if let Some(value) = &p.value {
8870            self.write(" = ");
8871            self.generate_expression(value)?;
8872        } else if !p.args.is_empty() {
8873            self.write("(");
8874            for (i, arg) in p.args.iter().enumerate() {
8875                if i > 0 {
8876                    self.write(", ");
8877                }
8878                self.generate_expression(arg)?;
8879            }
8880            self.write(")");
8881        }
8882
8883        Ok(())
8884    }
8885
8886    fn generate_grant(&mut self, g: &Grant) -> Result<()> {
8887        self.write_keyword("GRANT");
8888        self.write_space();
8889
8890        // Privileges (with optional column lists)
8891        for (i, privilege) in g.privileges.iter().enumerate() {
8892            if i > 0 {
8893                self.write(", ");
8894            }
8895            self.write_keyword(&privilege.name);
8896            // Output column list if present: SELECT(col1, col2)
8897            if !privilege.columns.is_empty() {
8898                self.write("(");
8899                for (j, col) in privilege.columns.iter().enumerate() {
8900                    if j > 0 {
8901                        self.write(", ");
8902                    }
8903                    self.write(col);
8904                }
8905                self.write(")");
8906            }
8907        }
8908
8909        self.write_space();
8910        self.write_keyword("ON");
8911        self.write_space();
8912
8913        // Object kind (TABLE, SCHEMA, etc.)
8914        if let Some(kind) = &g.kind {
8915            self.write_keyword(kind);
8916            self.write_space();
8917        }
8918
8919        // Securable - normalize function/procedure names to uppercase for PostgreSQL family
8920        {
8921            use crate::dialects::DialectType;
8922            let should_upper = matches!(
8923                self.config.dialect,
8924                Some(DialectType::PostgreSQL)
8925                    | Some(DialectType::CockroachDB)
8926                    | Some(DialectType::Materialize)
8927                    | Some(DialectType::RisingWave)
8928            ) && (g.kind.as_deref() == Some("FUNCTION") || g.kind.as_deref() == Some("PROCEDURE"));
8929            if should_upper {
8930                use crate::expressions::Identifier;
8931                let upper_id = Identifier {
8932                    name: g.securable.name.to_uppercase(),
8933                    quoted: g.securable.quoted,
8934                    ..g.securable.clone()
8935                };
8936                self.generate_identifier(&upper_id)?;
8937            } else {
8938                self.generate_identifier(&g.securable)?;
8939            }
8940        }
8941
8942        // Function parameter types (if present)
8943        if !g.function_params.is_empty() {
8944            self.write("(");
8945            for (i, param) in g.function_params.iter().enumerate() {
8946                if i > 0 {
8947                    self.write(", ");
8948                }
8949                self.write(param);
8950            }
8951            self.write(")");
8952        }
8953
8954        self.write_space();
8955        self.write_keyword("TO");
8956        self.write_space();
8957
8958        // Principals
8959        for (i, principal) in g.principals.iter().enumerate() {
8960            if i > 0 {
8961                self.write(", ");
8962            }
8963            if principal.is_role {
8964                self.write_keyword("ROLE");
8965                self.write_space();
8966            } else if principal.is_group {
8967                self.write_keyword("GROUP");
8968                self.write_space();
8969            }
8970            self.generate_identifier(&principal.name)?;
8971        }
8972
8973        // WITH GRANT OPTION
8974        if g.grant_option {
8975            self.write_space();
8976            self.write_keyword("WITH GRANT OPTION");
8977        }
8978
8979        // TSQL: AS principal
8980        if let Some(ref principal) = g.as_principal {
8981            self.write_space();
8982            self.write_keyword("AS");
8983            self.write_space();
8984            self.generate_identifier(principal)?;
8985        }
8986
8987        Ok(())
8988    }
8989
8990    fn generate_revoke(&mut self, r: &Revoke) -> Result<()> {
8991        self.write_keyword("REVOKE");
8992        self.write_space();
8993
8994        // GRANT OPTION FOR
8995        if r.grant_option {
8996            self.write_keyword("GRANT OPTION FOR");
8997            self.write_space();
8998        }
8999
9000        // Privileges (with optional column lists)
9001        for (i, privilege) in r.privileges.iter().enumerate() {
9002            if i > 0 {
9003                self.write(", ");
9004            }
9005            self.write_keyword(&privilege.name);
9006            // Output column list if present: SELECT(col1, col2)
9007            if !privilege.columns.is_empty() {
9008                self.write("(");
9009                for (j, col) in privilege.columns.iter().enumerate() {
9010                    if j > 0 {
9011                        self.write(", ");
9012                    }
9013                    self.write(col);
9014                }
9015                self.write(")");
9016            }
9017        }
9018
9019        self.write_space();
9020        self.write_keyword("ON");
9021        self.write_space();
9022
9023        // Object kind
9024        if let Some(kind) = &r.kind {
9025            self.write_keyword(kind);
9026            self.write_space();
9027        }
9028
9029        // Securable - normalize function/procedure names to uppercase for PostgreSQL family
9030        {
9031            use crate::dialects::DialectType;
9032            let should_upper = matches!(
9033                self.config.dialect,
9034                Some(DialectType::PostgreSQL)
9035                    | Some(DialectType::CockroachDB)
9036                    | Some(DialectType::Materialize)
9037                    | Some(DialectType::RisingWave)
9038            ) && (r.kind.as_deref() == Some("FUNCTION") || r.kind.as_deref() == Some("PROCEDURE"));
9039            if should_upper {
9040                use crate::expressions::Identifier;
9041                let upper_id = Identifier {
9042                    name: r.securable.name.to_uppercase(),
9043                    quoted: r.securable.quoted,
9044                    ..r.securable.clone()
9045                };
9046                self.generate_identifier(&upper_id)?;
9047            } else {
9048                self.generate_identifier(&r.securable)?;
9049            }
9050        }
9051
9052        // Function parameter types (if present)
9053        if !r.function_params.is_empty() {
9054            self.write("(");
9055            for (i, param) in r.function_params.iter().enumerate() {
9056                if i > 0 {
9057                    self.write(", ");
9058                }
9059                self.write(param);
9060            }
9061            self.write(")");
9062        }
9063
9064        self.write_space();
9065        self.write_keyword("FROM");
9066        self.write_space();
9067
9068        // Principals
9069        for (i, principal) in r.principals.iter().enumerate() {
9070            if i > 0 {
9071                self.write(", ");
9072            }
9073            if principal.is_role {
9074                self.write_keyword("ROLE");
9075                self.write_space();
9076            } else if principal.is_group {
9077                self.write_keyword("GROUP");
9078                self.write_space();
9079            }
9080            self.generate_identifier(&principal.name)?;
9081        }
9082
9083        // CASCADE or RESTRICT
9084        if r.cascade {
9085            self.write_space();
9086            self.write_keyword("CASCADE");
9087        } else if r.restrict {
9088            self.write_space();
9089            self.write_keyword("RESTRICT");
9090        }
9091
9092        Ok(())
9093    }
9094
9095    fn generate_comment(&mut self, c: &Comment) -> Result<()> {
9096        self.write_keyword("COMMENT");
9097
9098        // IF EXISTS
9099        if c.exists {
9100            self.write_space();
9101            self.write_keyword("IF EXISTS");
9102        }
9103
9104        self.write_space();
9105        self.write_keyword("ON");
9106
9107        // MATERIALIZED
9108        if c.materialized {
9109            self.write_space();
9110            self.write_keyword("MATERIALIZED");
9111        }
9112
9113        self.write_space();
9114        self.write_keyword(&c.kind);
9115        self.write_space();
9116
9117        // Object name
9118        self.generate_expression(&c.this)?;
9119
9120        self.write_space();
9121        self.write_keyword("IS");
9122        self.write_space();
9123
9124        // Comment expression
9125        self.generate_expression(&c.expression)?;
9126
9127        Ok(())
9128    }
9129
9130    fn generate_set_statement(&mut self, s: &SetStatement) -> Result<()> {
9131        self.write_keyword("SET");
9132
9133        for (i, item) in s.items.iter().enumerate() {
9134            if i > 0 {
9135                self.write(",");
9136            }
9137            self.write_space();
9138
9139            // Kind modifier (GLOBAL, LOCAL, SESSION, PERSIST, PERSIST_ONLY)
9140            if let Some(ref kind) = item.kind {
9141                self.write_keyword(kind);
9142                self.write_space();
9143            }
9144
9145            // Check for special SET forms by name
9146            let name_str = match &item.name {
9147                Expression::Identifier(id) => Some(id.name.as_str()),
9148                _ => None,
9149            };
9150
9151            let is_transaction = name_str == Some("TRANSACTION");
9152            let is_character_set = name_str == Some("CHARACTER SET");
9153            let is_names = name_str == Some("NAMES");
9154            let is_collate = name_str == Some("COLLATE");
9155            let has_variable_kind = item.kind.as_deref() == Some("VARIABLE");
9156            let name_has_variable_prefix = name_str.map_or(false, |n| n.starts_with("VARIABLE "));
9157            let is_variable = has_variable_kind || name_has_variable_prefix;
9158            let is_value_only = matches!(&item.value, Expression::Identifier(id) if id.name.is_empty());
9159
9160            if is_transaction {
9161                // Output: SET [GLOBAL|SESSION] TRANSACTION <characteristics>
9162                self.write_keyword("TRANSACTION");
9163                if let Expression::Identifier(id) = &item.value {
9164                    if !id.name.is_empty() {
9165                        self.write_space();
9166                        self.write(&id.name);
9167                    }
9168                }
9169            } else if is_character_set {
9170                // Output: SET CHARACTER SET <charset>
9171                self.write_keyword("CHARACTER SET");
9172                self.write_space();
9173                self.generate_set_value(&item.value)?;
9174            } else if is_names {
9175                // Output: SET NAMES <charset>
9176                self.write_keyword("NAMES");
9177                self.write_space();
9178                self.generate_set_value(&item.value)?;
9179            } else if is_collate {
9180                // Output: COLLATE <collation> (part of SET NAMES ... COLLATE ...)
9181                self.write_keyword("COLLATE");
9182                self.write_space();
9183                self.generate_set_value(&item.value)?;
9184            } else if is_variable {
9185                // Output: SET [VARIABLE] <name> = <value>
9186                // If kind=VARIABLE, the keyword was already written above.
9187                // If name has VARIABLE prefix, write VARIABLE keyword for DuckDB target only.
9188                if name_has_variable_prefix && !has_variable_kind {
9189                    if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
9190                        self.write_keyword("VARIABLE");
9191                        self.write_space();
9192                    }
9193                }
9194                // Extract actual variable name (strip VARIABLE prefix if present)
9195                if let Some(ns) = name_str {
9196                    let var_name = if name_has_variable_prefix {
9197                        &ns["VARIABLE ".len()..]
9198                    } else {
9199                        ns
9200                    };
9201                    self.write(var_name);
9202                } else {
9203                    self.generate_expression(&item.name)?;
9204                }
9205                self.write(" = ");
9206                self.generate_set_value(&item.value)?;
9207            } else if is_value_only {
9208                // SET <name> ON/OFF without = (TSQL: SET XACT_ABORT ON)
9209                self.generate_expression(&item.name)?;
9210            } else if item.no_equals && matches!(self.config.dialect, Some(DialectType::TSQL)) {
9211                // SET key value without = (TSQL style)
9212                self.generate_expression(&item.name)?;
9213                self.write_space();
9214                self.generate_set_value(&item.value)?;
9215            } else {
9216                // Standard: variable = value
9217                // SET item names should not be quoted (they are config parameter names, not column refs)
9218                match &item.name {
9219                    Expression::Identifier(id) => {
9220                        self.write(&id.name);
9221                    }
9222                    _ => {
9223                        self.generate_expression(&item.name)?;
9224                    }
9225                }
9226                self.write(" = ");
9227                self.generate_set_value(&item.value)?;
9228            }
9229        }
9230
9231        Ok(())
9232    }
9233
9234    /// Generate a SET statement value, writing keyword values (DEFAULT, ON, OFF)
9235    /// directly to avoid reserved keyword quoting.
9236    fn generate_set_value(&mut self, value: &Expression) -> Result<()> {
9237        if let Expression::Identifier(id) = value {
9238            match id.name.as_str() {
9239                "DEFAULT" | "ON" | "OFF" => {
9240                    self.write_keyword(&id.name);
9241                    return Ok(());
9242                }
9243                _ => {}
9244            }
9245        }
9246        self.generate_expression(value)
9247    }
9248
9249    // ==================== Phase 4: Additional DDL Generation ====================
9250
9251    fn generate_alter_view(&mut self, av: &AlterView) -> Result<()> {
9252        self.write_keyword("ALTER");
9253        // MySQL modifiers before VIEW
9254        if let Some(ref algorithm) = av.algorithm {
9255            self.write_space();
9256            self.write_keyword("ALGORITHM");
9257            self.write(" = ");
9258            self.write_keyword(algorithm);
9259        }
9260        if let Some(ref definer) = av.definer {
9261            self.write_space();
9262            self.write_keyword("DEFINER");
9263            self.write(" = ");
9264            self.write(definer);
9265        }
9266        if let Some(ref sql_security) = av.sql_security {
9267            self.write_space();
9268            self.write_keyword("SQL SECURITY");
9269            self.write(" = ");
9270            self.write_keyword(sql_security);
9271        }
9272        self.write_space();
9273        self.write_keyword("VIEW");
9274        self.write_space();
9275        self.generate_table(&av.name)?;
9276
9277        // Hive: Column aliases with optional COMMENT
9278        if !av.columns.is_empty() {
9279            self.write(" (");
9280            for (i, col) in av.columns.iter().enumerate() {
9281                if i > 0 {
9282                    self.write(", ");
9283                }
9284                self.generate_identifier(&col.name)?;
9285                if let Some(ref comment) = col.comment {
9286                    self.write_space();
9287                    self.write_keyword("COMMENT");
9288                    self.write(" ");
9289                    self.generate_string_literal(comment)?;
9290                }
9291            }
9292            self.write(")");
9293        }
9294
9295        // TSQL: WITH option before actions
9296        if let Some(ref opt) = av.with_option {
9297            self.write_space();
9298            self.write_keyword("WITH");
9299            self.write_space();
9300            self.write_keyword(opt);
9301        }
9302
9303        for action in &av.actions {
9304            self.write_space();
9305            match action {
9306                AlterViewAction::Rename(new_name) => {
9307                    self.write_keyword("RENAME TO");
9308                    self.write_space();
9309                    self.generate_table(new_name)?;
9310                }
9311                AlterViewAction::OwnerTo(owner) => {
9312                    self.write_keyword("OWNER TO");
9313                    self.write_space();
9314                    self.generate_identifier(owner)?;
9315                }
9316                AlterViewAction::SetSchema(schema) => {
9317                    self.write_keyword("SET SCHEMA");
9318                    self.write_space();
9319                    self.generate_identifier(schema)?;
9320                }
9321                AlterViewAction::SetAuthorization(auth) => {
9322                    self.write_keyword("SET AUTHORIZATION");
9323                    self.write_space();
9324                    self.write(auth);
9325                }
9326                AlterViewAction::AlterColumn { name, action } => {
9327                    self.write_keyword("ALTER COLUMN");
9328                    self.write_space();
9329                    self.generate_identifier(name)?;
9330                    self.write_space();
9331                    self.generate_alter_column_action(action)?;
9332                }
9333                AlterViewAction::AsSelect(query) => {
9334                    self.write_keyword("AS");
9335                    self.write_space();
9336                    self.generate_expression(query)?;
9337                }
9338                AlterViewAction::SetTblproperties(props) => {
9339                    self.write_keyword("SET TBLPROPERTIES");
9340                    self.write(" (");
9341                    for (i, (key, value)) in props.iter().enumerate() {
9342                        if i > 0 {
9343                            self.write(", ");
9344                        }
9345                        self.generate_string_literal(key)?;
9346                        self.write("=");
9347                        self.generate_string_literal(value)?;
9348                    }
9349                    self.write(")");
9350                }
9351                AlterViewAction::UnsetTblproperties(keys) => {
9352                    self.write_keyword("UNSET TBLPROPERTIES");
9353                    self.write(" (");
9354                    for (i, key) in keys.iter().enumerate() {
9355                        if i > 0 {
9356                            self.write(", ");
9357                        }
9358                        self.generate_string_literal(key)?;
9359                    }
9360                    self.write(")");
9361                }
9362            }
9363        }
9364
9365        Ok(())
9366    }
9367
9368    fn generate_alter_index(&mut self, ai: &AlterIndex) -> Result<()> {
9369        self.write_keyword("ALTER INDEX");
9370        self.write_space();
9371        self.generate_identifier(&ai.name)?;
9372
9373        if let Some(table) = &ai.table {
9374            self.write_space();
9375            self.write_keyword("ON");
9376            self.write_space();
9377            self.generate_table(table)?;
9378        }
9379
9380        for action in &ai.actions {
9381            self.write_space();
9382            match action {
9383                AlterIndexAction::Rename(new_name) => {
9384                    self.write_keyword("RENAME TO");
9385                    self.write_space();
9386                    self.generate_identifier(new_name)?;
9387                }
9388                AlterIndexAction::SetTablespace(tablespace) => {
9389                    self.write_keyword("SET TABLESPACE");
9390                    self.write_space();
9391                    self.generate_identifier(tablespace)?;
9392                }
9393                AlterIndexAction::Visible(visible) => {
9394                    if *visible {
9395                        self.write_keyword("VISIBLE");
9396                    } else {
9397                        self.write_keyword("INVISIBLE");
9398                    }
9399                }
9400            }
9401        }
9402
9403        Ok(())
9404    }
9405
9406    fn generate_create_schema(&mut self, cs: &CreateSchema) -> Result<()> {
9407        // Output leading comments
9408        for comment in &cs.leading_comments {
9409            self.write(comment);
9410            self.write_space();
9411        }
9412
9413        // Athena: CREATE SCHEMA uses Hive engine (backticks)
9414        let saved_athena_hive_context = self.athena_hive_context;
9415        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
9416            self.athena_hive_context = true;
9417        }
9418
9419        self.write_keyword("CREATE SCHEMA");
9420
9421        if cs.if_not_exists {
9422            self.write_space();
9423            self.write_keyword("IF NOT EXISTS");
9424        }
9425
9426        self.write_space();
9427        self.generate_identifier(&cs.name)?;
9428
9429        if let Some(ref clone_src) = cs.clone_from {
9430            self.write_keyword(" CLONE ");
9431            self.generate_identifier(clone_src)?;
9432        }
9433
9434        if let Some(ref at_clause) = cs.at_clause {
9435            self.write_space();
9436            self.generate_expression(at_clause)?;
9437        }
9438
9439        if let Some(auth) = &cs.authorization {
9440            self.write_space();
9441            self.write_keyword("AUTHORIZATION");
9442            self.write_space();
9443            self.generate_identifier(auth)?;
9444        }
9445
9446        // Generate schema properties (e.g., DEFAULT COLLATE or WITH (props))
9447        // Separate WITH properties from other properties
9448        let with_properties: Vec<_> = cs.properties.iter()
9449            .filter(|p| matches!(p, Expression::Property(_)))
9450            .collect();
9451        let other_properties: Vec<_> = cs.properties.iter()
9452            .filter(|p| !matches!(p, Expression::Property(_)))
9453            .collect();
9454
9455        // Generate WITH (props) if we have Property expressions
9456        if !with_properties.is_empty() {
9457            self.write_space();
9458            self.write_keyword("WITH");
9459            self.write(" (");
9460            for (i, prop) in with_properties.iter().enumerate() {
9461                if i > 0 {
9462                    self.write(", ");
9463                }
9464                self.generate_expression(prop)?;
9465            }
9466            self.write(")");
9467        }
9468
9469        // Generate other properties (like DEFAULT COLLATE)
9470        for prop in other_properties {
9471            self.write_space();
9472            self.generate_expression(prop)?;
9473        }
9474
9475        // Restore Athena Hive context
9476        self.athena_hive_context = saved_athena_hive_context;
9477
9478        Ok(())
9479    }
9480
9481    fn generate_drop_schema(&mut self, ds: &DropSchema) -> Result<()> {
9482        self.write_keyword("DROP SCHEMA");
9483
9484        if ds.if_exists {
9485            self.write_space();
9486            self.write_keyword("IF EXISTS");
9487        }
9488
9489        self.write_space();
9490        self.generate_identifier(&ds.name)?;
9491
9492        if ds.cascade {
9493            self.write_space();
9494            self.write_keyword("CASCADE");
9495        }
9496
9497        Ok(())
9498    }
9499
9500    fn generate_drop_namespace(&mut self, dn: &DropNamespace) -> Result<()> {
9501        self.write_keyword("DROP NAMESPACE");
9502
9503        if dn.if_exists {
9504            self.write_space();
9505            self.write_keyword("IF EXISTS");
9506        }
9507
9508        self.write_space();
9509        self.generate_identifier(&dn.name)?;
9510
9511        if dn.cascade {
9512            self.write_space();
9513            self.write_keyword("CASCADE");
9514        }
9515
9516        Ok(())
9517    }
9518
9519    fn generate_create_database(&mut self, cd: &CreateDatabase) -> Result<()> {
9520        self.write_keyword("CREATE DATABASE");
9521
9522        if cd.if_not_exists {
9523            self.write_space();
9524            self.write_keyword("IF NOT EXISTS");
9525        }
9526
9527        self.write_space();
9528        self.generate_identifier(&cd.name)?;
9529
9530        if let Some(ref clone_src) = cd.clone_from {
9531            self.write_keyword(" CLONE ");
9532            self.generate_identifier(clone_src)?;
9533        }
9534
9535        // AT/BEFORE clause for time travel (Snowflake)
9536        if let Some(ref at_clause) = cd.at_clause {
9537            self.write_space();
9538            self.generate_expression(at_clause)?;
9539        }
9540
9541        for option in &cd.options {
9542            self.write_space();
9543            match option {
9544                DatabaseOption::CharacterSet(charset) => {
9545                    self.write_keyword("CHARACTER SET");
9546                    self.write(" = ");
9547                    self.write(&format!("'{}'", charset));
9548                }
9549                DatabaseOption::Collate(collate) => {
9550                    self.write_keyword("COLLATE");
9551                    self.write(" = ");
9552                    self.write(&format!("'{}'", collate));
9553                }
9554                DatabaseOption::Owner(owner) => {
9555                    self.write_keyword("OWNER");
9556                    self.write(" = ");
9557                    self.generate_identifier(owner)?;
9558                }
9559                DatabaseOption::Template(template) => {
9560                    self.write_keyword("TEMPLATE");
9561                    self.write(" = ");
9562                    self.generate_identifier(template)?;
9563                }
9564                DatabaseOption::Encoding(encoding) => {
9565                    self.write_keyword("ENCODING");
9566                    self.write(" = ");
9567                    self.write(&format!("'{}'", encoding));
9568                }
9569                DatabaseOption::Location(location) => {
9570                    self.write_keyword("LOCATION");
9571                    self.write(" = ");
9572                    self.write(&format!("'{}'", location));
9573                }
9574            }
9575        }
9576
9577        Ok(())
9578    }
9579
9580    fn generate_drop_database(&mut self, dd: &DropDatabase) -> Result<()> {
9581        self.write_keyword("DROP DATABASE");
9582
9583        if dd.if_exists {
9584            self.write_space();
9585            self.write_keyword("IF EXISTS");
9586        }
9587
9588        self.write_space();
9589        self.generate_identifier(&dd.name)?;
9590
9591        Ok(())
9592    }
9593
9594    fn generate_create_function(&mut self, cf: &CreateFunction) -> Result<()> {
9595        self.write_keyword("CREATE");
9596
9597        if cf.or_replace {
9598            self.write_space();
9599            self.write_keyword("OR REPLACE");
9600        }
9601
9602        if cf.temporary {
9603            self.write_space();
9604            self.write_keyword("TEMPORARY");
9605        }
9606
9607        self.write_space();
9608        if cf.is_table_function {
9609            self.write_keyword("TABLE FUNCTION");
9610        } else {
9611            self.write_keyword("FUNCTION");
9612        }
9613
9614        if cf.if_not_exists {
9615            self.write_space();
9616            self.write_keyword("IF NOT EXISTS");
9617        }
9618
9619        self.write_space();
9620        self.generate_table(&cf.name)?;
9621        if cf.has_parens {
9622            let func_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)) && !cf.parameters.is_empty();
9623            if func_multiline {
9624                self.write("(\n");
9625                self.indent_level += 2;
9626                self.write_indent();
9627                self.generate_function_parameters(&cf.parameters)?;
9628                self.write("\n");
9629                self.indent_level -= 2;
9630                self.write(")");
9631            } else {
9632                self.write("(");
9633                self.generate_function_parameters(&cf.parameters)?;
9634                self.write(")");
9635            }
9636        }
9637
9638        // Output RETURNS clause (always comes first after parameters)
9639        // BigQuery and TSQL use multiline formatting for CREATE FUNCTION structure
9640        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));
9641
9642        if cf.language_first {
9643            // LANGUAGE first, then SQL data access, then RETURNS
9644            if let Some(lang) = &cf.language {
9645                if use_multiline {
9646                    self.write_newline();
9647                } else {
9648                    self.write_space();
9649                }
9650                self.write_keyword("LANGUAGE");
9651                self.write_space();
9652                self.write(lang);
9653            }
9654
9655            // SQL data access comes after LANGUAGE in this case
9656            if let Some(sql_data) = &cf.sql_data_access {
9657                self.write_space();
9658                match sql_data {
9659                    SqlDataAccess::NoSql => self.write_keyword("NO SQL"),
9660                    SqlDataAccess::ContainsSql => self.write_keyword("CONTAINS SQL"),
9661                    SqlDataAccess::ReadsSqlData => self.write_keyword("READS SQL DATA"),
9662                    SqlDataAccess::ModifiesSqlData => self.write_keyword("MODIFIES SQL DATA"),
9663                }
9664            }
9665
9666            if let Some(ref rtb) = cf.returns_table_body {
9667                if use_multiline {
9668                    self.write_newline();
9669                } else {
9670                    self.write_space();
9671                }
9672                self.write_keyword("RETURNS");
9673                self.write_space();
9674                self.write(rtb);
9675            } else if let Some(return_type) = &cf.return_type {
9676                if use_multiline {
9677                    self.write_newline();
9678                } else {
9679                    self.write_space();
9680                }
9681                self.write_keyword("RETURNS");
9682                self.write_space();
9683                self.generate_data_type(return_type)?;
9684            }
9685        } else {
9686            // RETURNS first (default)
9687            // DuckDB macros: skip RETURNS output (empty marker in returns_table_body means TABLE return)
9688            let is_duckdb = matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB));
9689            if let Some(ref rtb) = cf.returns_table_body {
9690                if !(is_duckdb && rtb.is_empty()) {
9691                    if use_multiline {
9692                        self.write_newline();
9693                    } else {
9694                        self.write_space();
9695                    }
9696                    self.write_keyword("RETURNS");
9697                    self.write_space();
9698                    self.write(rtb);
9699                }
9700            } else if let Some(return_type) = &cf.return_type {
9701                if use_multiline {
9702                    self.write_newline();
9703                } else {
9704                    self.write_space();
9705                }
9706                self.write_keyword("RETURNS");
9707                self.write_space();
9708                self.generate_data_type(return_type)?;
9709            }
9710        }
9711
9712        // If we have property_order, use it to output properties in original order
9713        if !cf.property_order.is_empty() {
9714            // For BigQuery, OPTIONS must come before AS - reorder if needed
9715            let is_bigquery = matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery));
9716            let property_order = if is_bigquery {
9717                // Move Options before As if both are present
9718                let mut reordered = Vec::new();
9719                let mut has_as = false;
9720                let mut has_options = false;
9721                for prop in &cf.property_order {
9722                    match prop {
9723                        FunctionPropertyKind::As => has_as = true,
9724                        FunctionPropertyKind::Options => has_options = true,
9725                        _ => {}
9726                    }
9727                }
9728                if has_as && has_options {
9729                    // Output all props except As and Options, then Options, then As
9730                    for prop in &cf.property_order {
9731                        if *prop != FunctionPropertyKind::As && *prop != FunctionPropertyKind::Options {
9732                            reordered.push(*prop);
9733                        }
9734                    }
9735                    reordered.push(FunctionPropertyKind::Options);
9736                    reordered.push(FunctionPropertyKind::As);
9737                    reordered
9738                } else {
9739                    cf.property_order.clone()
9740                }
9741            } else {
9742                cf.property_order.clone()
9743            };
9744
9745            for prop in &property_order {
9746                match prop {
9747                    FunctionPropertyKind::Set => {
9748                        self.generate_function_set_options(cf)?;
9749                    }
9750                    FunctionPropertyKind::As => {
9751                        self.generate_function_body(cf)?;
9752                    }
9753                    FunctionPropertyKind::Language => {
9754                        if !cf.language_first {
9755                            // Only output here if not already output above
9756                            if let Some(lang) = &cf.language {
9757                                // Only BigQuery uses multiline formatting
9758                                let use_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery));
9759                                if use_multiline {
9760                                    self.write_newline();
9761                                } else {
9762                                    self.write_space();
9763                                }
9764                                self.write_keyword("LANGUAGE");
9765                                self.write_space();
9766                                self.write(lang);
9767                            }
9768                        }
9769                    }
9770                    FunctionPropertyKind::Determinism => {
9771                        self.generate_function_determinism(cf)?;
9772                    }
9773                    FunctionPropertyKind::NullInput => {
9774                        self.generate_function_null_input(cf)?;
9775                    }
9776                    FunctionPropertyKind::Security => {
9777                        self.generate_function_security(cf)?;
9778                    }
9779                    FunctionPropertyKind::SqlDataAccess => {
9780                        if !cf.language_first {
9781                            // Only output here if not already output above
9782                            self.generate_function_sql_data_access(cf)?;
9783                        }
9784                    }
9785                    FunctionPropertyKind::Options => {
9786                        if !cf.options.is_empty() {
9787                            self.write_space();
9788                            self.generate_options_clause(&cf.options)?;
9789                        }
9790                    }
9791                    FunctionPropertyKind::Environment => {
9792                        if !cf.environment.is_empty() {
9793                            self.write_space();
9794                            self.generate_environment_clause(&cf.environment)?;
9795                        }
9796                    }
9797                }
9798            }
9799
9800            // Output OPTIONS if not tracked in property_order (legacy)
9801            if !cf.options.is_empty() && !cf.property_order.contains(&FunctionPropertyKind::Options) {
9802                self.write_space();
9803                self.generate_options_clause(&cf.options)?;
9804            }
9805
9806            // Output ENVIRONMENT if not tracked in property_order (legacy)
9807            if !cf.environment.is_empty() && !cf.property_order.contains(&FunctionPropertyKind::Environment) {
9808                self.write_space();
9809                self.generate_environment_clause(&cf.environment)?;
9810            }
9811        } else {
9812            // Legacy behavior when property_order is empty
9813            // BigQuery: DETERMINISTIC/NOT DETERMINISTIC comes before LANGUAGE
9814            if matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) {
9815                self.generate_function_determinism(cf)?;
9816            }
9817
9818            // Only BigQuery uses multiline formatting for CREATE FUNCTION structure
9819            let use_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery));
9820
9821            if !cf.language_first {
9822                if let Some(lang) = &cf.language {
9823                    if use_multiline {
9824                        self.write_newline();
9825                    } else {
9826                        self.write_space();
9827                    }
9828                    self.write_keyword("LANGUAGE");
9829                    self.write_space();
9830                    self.write(lang);
9831                }
9832
9833                // SQL data access characteristic comes after LANGUAGE
9834                self.generate_function_sql_data_access(cf)?;
9835            }
9836
9837            // For non-BigQuery dialects, output DETERMINISTIC/IMMUTABLE/VOLATILE here
9838            if !matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) {
9839                self.generate_function_determinism(cf)?;
9840            }
9841
9842            self.generate_function_null_input(cf)?;
9843            self.generate_function_security(cf)?;
9844            self.generate_function_set_options(cf)?;
9845
9846            // BigQuery: OPTIONS (key=value, ...) - comes before AS
9847            if !cf.options.is_empty() {
9848                self.write_space();
9849                self.generate_options_clause(&cf.options)?;
9850            }
9851
9852            // Databricks: ENVIRONMENT (dependencies = '...', ...) - comes before AS
9853            if !cf.environment.is_empty() {
9854                self.write_space();
9855                self.generate_environment_clause(&cf.environment)?;
9856            }
9857
9858            self.generate_function_body(cf)?;
9859        }
9860
9861        Ok(())
9862    }
9863
9864    /// Generate SET options for CREATE FUNCTION
9865    fn generate_function_set_options(&mut self, cf: &CreateFunction) -> Result<()> {
9866        for opt in &cf.set_options {
9867            self.write_space();
9868            self.write_keyword("SET");
9869            self.write_space();
9870            self.write(&opt.name);
9871            match &opt.value {
9872                FunctionSetValue::Value { value, use_to } => {
9873                    // PostgreSQL normalizes TO to = (matching Python sqlglot behavior)
9874                    if *use_to && !matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL)) {
9875                        self.write(" TO ");
9876                    } else {
9877                        self.write(" = ");
9878                    }
9879                    self.write(value);
9880                }
9881                FunctionSetValue::FromCurrent => {
9882                    self.write_space();
9883                    self.write_keyword("FROM CURRENT");
9884                }
9885            }
9886        }
9887        Ok(())
9888    }
9889
9890    /// Generate function body (AS clause)
9891    fn generate_function_body(&mut self, cf: &CreateFunction) -> Result<()> {
9892        if let Some(body) = &cf.body {
9893            // AS stays on same line as previous content (e.g., LANGUAGE js AS)
9894            self.write_space();
9895            // Only BigQuery uses multiline formatting for CREATE FUNCTION body
9896            let use_multiline = self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery));
9897            match body {
9898                FunctionBody::Block(block) => {
9899                    self.write_keyword("AS");
9900                    if matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL)) {
9901                        self.write(" BEGIN ");
9902                        self.write(block);
9903                        self.write(" END");
9904                    } else if matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL)) {
9905                        self.write(" $$");
9906                        self.write(block);
9907                        self.write("$$");
9908                    } else {
9909                        // Escape content for single-quoted output
9910                        let escaped = self.escape_block_for_single_quote(block);
9911                        // In BigQuery pretty mode, body content goes on new line
9912                        if use_multiline {
9913                            self.write_newline();
9914                        } else {
9915                            self.write(" ");
9916                        }
9917                        self.write("'");
9918                        self.write(&escaped);
9919                        self.write("'");
9920                    }
9921                }
9922                FunctionBody::StringLiteral(s) => {
9923                    self.write_keyword("AS");
9924                    // In BigQuery pretty mode, body content goes on new line
9925                    if use_multiline {
9926                        self.write_newline();
9927                    } else {
9928                        self.write(" ");
9929                    }
9930                    self.write("'");
9931                    self.write(s);
9932                    self.write("'");
9933                }
9934                FunctionBody::Expression(expr) => {
9935                    self.write_keyword("AS");
9936                    self.write_space();
9937                    self.generate_expression(expr)?;
9938                }
9939                FunctionBody::External(name) => {
9940                    self.write_keyword("EXTERNAL NAME");
9941                    self.write(" '");
9942                    self.write(name);
9943                    self.write("'");
9944                }
9945                FunctionBody::Return(expr) => {
9946                    if matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB)) {
9947                        // DuckDB macro syntax: AS [TABLE] expression (no RETURN keyword)
9948                        self.write_keyword("AS");
9949                        self.write_space();
9950                        // Empty returns_table_body signals TABLE return
9951                        if cf.returns_table_body.is_some() {
9952                            self.write_keyword("TABLE");
9953                            self.write_space();
9954                        }
9955                        self.generate_expression(expr)?;
9956                    } else {
9957                        if self.config.create_function_return_as {
9958                            self.write_keyword("AS");
9959                            // TSQL pretty: newline between AS and RETURN
9960                            if self.config.pretty && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Fabric)) {
9961                                self.write_newline();
9962                            } else {
9963                                self.write_space();
9964                            }
9965                        }
9966                        self.write_keyword("RETURN");
9967                        self.write_space();
9968                        self.generate_expression(expr)?;
9969                    }
9970                }
9971                FunctionBody::Statements(stmts) => {
9972                    self.write_keyword("AS");
9973                    self.write(" BEGIN ");
9974                    for (i, stmt) in stmts.iter().enumerate() {
9975                        if i > 0 {
9976                            self.write(" ");
9977                        }
9978                        self.generate_expression(stmt)?;
9979                    }
9980                    self.write(" END");
9981                }
9982                FunctionBody::DollarQuoted { content, tag } => {
9983                    self.write_keyword("AS");
9984                    self.write(" ");
9985                    // Dialects that support dollar-quoted strings: PostgreSQL, Databricks, Redshift, DuckDB
9986                    let supports_dollar_quoting = matches!(
9987                        self.config.dialect,
9988                        Some(crate::dialects::DialectType::PostgreSQL)
9989                            | Some(crate::dialects::DialectType::Databricks)
9990                            | Some(crate::dialects::DialectType::Redshift)
9991                            | Some(crate::dialects::DialectType::DuckDB)
9992                    );
9993                    if supports_dollar_quoting {
9994                        // Output in dollar-quoted format
9995                        self.write("$");
9996                        if let Some(t) = tag {
9997                            self.write(t);
9998                        }
9999                        self.write("$");
10000                        self.write(content);
10001                        self.write("$");
10002                        if let Some(t) = tag {
10003                            self.write(t);
10004                        }
10005                        self.write("$");
10006                    } else {
10007                        // Convert to single-quoted string for other dialects
10008                        let escaped = self.escape_block_for_single_quote(content);
10009                        self.write("'");
10010                        self.write(&escaped);
10011                        self.write("'");
10012                    }
10013                }
10014            }
10015        }
10016        Ok(())
10017    }
10018
10019    /// Generate determinism clause (IMMUTABLE/VOLATILE/DETERMINISTIC)
10020    fn generate_function_determinism(&mut self, cf: &CreateFunction) -> Result<()> {
10021        if let Some(det) = cf.deterministic {
10022            self.write_space();
10023            if matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)) {
10024                // BigQuery uses DETERMINISTIC/NOT DETERMINISTIC
10025                if det {
10026                    self.write_keyword("DETERMINISTIC");
10027                } else {
10028                    self.write_keyword("NOT DETERMINISTIC");
10029                }
10030            } else {
10031                // PostgreSQL and others use IMMUTABLE/VOLATILE
10032                if det {
10033                    self.write_keyword("IMMUTABLE");
10034                } else {
10035                    self.write_keyword("VOLATILE");
10036                }
10037            }
10038        }
10039        Ok(())
10040    }
10041
10042    /// Generate null input handling clause
10043    fn generate_function_null_input(&mut self, cf: &CreateFunction) -> Result<()> {
10044        if let Some(returns_null) = cf.returns_null_on_null_input {
10045            self.write_space();
10046            if returns_null {
10047                if cf.strict {
10048                    self.write_keyword("STRICT");
10049                } else {
10050                    self.write_keyword("RETURNS NULL ON NULL INPUT");
10051                }
10052            } else {
10053                self.write_keyword("CALLED ON NULL INPUT");
10054            }
10055        }
10056        Ok(())
10057    }
10058
10059    /// Generate security clause
10060    fn generate_function_security(&mut self, cf: &CreateFunction) -> Result<()> {
10061        if let Some(security) = &cf.security {
10062            self.write_space();
10063            self.write_keyword("SECURITY");
10064            self.write_space();
10065            match security {
10066                FunctionSecurity::Definer => self.write_keyword("DEFINER"),
10067                FunctionSecurity::Invoker => self.write_keyword("INVOKER"),
10068                FunctionSecurity::None => self.write_keyword("NONE"),
10069            }
10070        }
10071        Ok(())
10072    }
10073
10074    /// Generate SQL data access clause
10075    fn generate_function_sql_data_access(&mut self, cf: &CreateFunction) -> Result<()> {
10076        if let Some(sql_data) = &cf.sql_data_access {
10077            self.write_space();
10078            match sql_data {
10079                SqlDataAccess::NoSql => self.write_keyword("NO SQL"),
10080                SqlDataAccess::ContainsSql => self.write_keyword("CONTAINS SQL"),
10081                SqlDataAccess::ReadsSqlData => self.write_keyword("READS SQL DATA"),
10082                SqlDataAccess::ModifiesSqlData => self.write_keyword("MODIFIES SQL DATA"),
10083            }
10084        }
10085        Ok(())
10086    }
10087
10088    fn generate_function_parameters(&mut self, params: &[FunctionParameter]) -> Result<()> {
10089        for (i, param) in params.iter().enumerate() {
10090            if i > 0 {
10091                self.write(", ");
10092            }
10093
10094            if let Some(mode) = &param.mode {
10095                match mode {
10096                    ParameterMode::In => self.write_keyword("IN"),
10097                    ParameterMode::Out => self.write_keyword("OUT"),
10098                    ParameterMode::InOut => self.write_keyword("INOUT"),
10099                }
10100                self.write_space();
10101            }
10102
10103            if let Some(name) = &param.name {
10104                self.generate_identifier(name)?;
10105                // Skip space and type for empty Custom types (e.g., DuckDB macros)
10106                let skip_type = matches!(&param.data_type, DataType::Custom { name } if name.is_empty());
10107                if !skip_type {
10108                    self.write_space();
10109                    self.generate_data_type(&param.data_type)?;
10110                }
10111            } else {
10112                self.generate_data_type(&param.data_type)?;
10113            }
10114
10115            if let Some(default) = &param.default {
10116                if self.config.parameter_default_equals {
10117                    self.write(" = ");
10118                } else {
10119                    self.write(" DEFAULT ");
10120                }
10121                self.generate_expression(default)?;
10122            }
10123        }
10124
10125        Ok(())
10126    }
10127
10128    fn generate_drop_function(&mut self, df: &DropFunction) -> Result<()> {
10129        self.write_keyword("DROP FUNCTION");
10130
10131        if df.if_exists {
10132            self.write_space();
10133            self.write_keyword("IF EXISTS");
10134        }
10135
10136        self.write_space();
10137        self.generate_table(&df.name)?;
10138
10139        if let Some(params) = &df.parameters {
10140            self.write(" (");
10141            for (i, dt) in params.iter().enumerate() {
10142                if i > 0 {
10143                    self.write(", ");
10144                }
10145                self.generate_data_type(dt)?;
10146            }
10147            self.write(")");
10148        }
10149
10150        if df.cascade {
10151            self.write_space();
10152            self.write_keyword("CASCADE");
10153        }
10154
10155        Ok(())
10156    }
10157
10158    fn generate_create_procedure(&mut self, cp: &CreateProcedure) -> Result<()> {
10159        self.write_keyword("CREATE");
10160
10161        if cp.or_replace {
10162            self.write_space();
10163            self.write_keyword("OR REPLACE");
10164        }
10165
10166        self.write_space();
10167        if cp.use_proc_keyword {
10168            self.write_keyword("PROC");
10169        } else {
10170            self.write_keyword("PROCEDURE");
10171        }
10172
10173        if cp.if_not_exists {
10174            self.write_space();
10175            self.write_keyword("IF NOT EXISTS");
10176        }
10177
10178        self.write_space();
10179        self.generate_table(&cp.name)?;
10180        if cp.has_parens {
10181            self.write("(");
10182            self.generate_function_parameters(&cp.parameters)?;
10183            self.write(")");
10184        } else if !cp.parameters.is_empty() {
10185            // TSQL: unparenthesized parameters
10186            self.write_space();
10187            self.generate_function_parameters(&cp.parameters)?;
10188        }
10189
10190        // RETURNS clause (Snowflake)
10191        if let Some(return_type) = &cp.return_type {
10192            self.write_space();
10193            self.write_keyword("RETURNS");
10194            self.write_space();
10195            self.generate_data_type(return_type)?;
10196        }
10197
10198        // EXECUTE AS clause (Snowflake)
10199        if let Some(execute_as) = &cp.execute_as {
10200            self.write_space();
10201            self.write_keyword("EXECUTE AS");
10202            self.write_space();
10203            self.write_keyword(execute_as);
10204        }
10205
10206        if let Some(lang) = &cp.language {
10207            self.write_space();
10208            self.write_keyword("LANGUAGE");
10209            self.write_space();
10210            self.write(lang);
10211        }
10212
10213        if let Some(security) = &cp.security {
10214            self.write_space();
10215            self.write_keyword("SECURITY");
10216            self.write_space();
10217            match security {
10218                FunctionSecurity::Definer => self.write_keyword("DEFINER"),
10219                FunctionSecurity::Invoker => self.write_keyword("INVOKER"),
10220                FunctionSecurity::None => self.write_keyword("NONE"),
10221            }
10222        }
10223
10224        // TSQL WITH options (ENCRYPTION, RECOMPILE, etc.)
10225        if !cp.with_options.is_empty() {
10226            self.write_space();
10227            self.write_keyword("WITH");
10228            self.write_space();
10229            for (i, opt) in cp.with_options.iter().enumerate() {
10230                if i > 0 {
10231                    self.write(", ");
10232                }
10233                self.write(opt);
10234            }
10235        }
10236
10237        if let Some(body) = &cp.body {
10238            self.write_space();
10239            match body {
10240                FunctionBody::Block(block) => {
10241                    self.write_keyword("AS");
10242                    if matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL)) {
10243                        self.write(" BEGIN ");
10244                        self.write(block);
10245                        self.write(" END");
10246                    } else if matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL)) {
10247                        self.write(" $$");
10248                        self.write(block);
10249                        self.write("$$");
10250                    } else {
10251                        // Escape content for single-quoted output
10252                        let escaped = self.escape_block_for_single_quote(block);
10253                        self.write(" '");
10254                        self.write(&escaped);
10255                        self.write("'");
10256                    }
10257                }
10258                FunctionBody::StringLiteral(s) => {
10259                    self.write_keyword("AS");
10260                    self.write(" '");
10261                    self.write(s);
10262                    self.write("'");
10263                }
10264                FunctionBody::Expression(expr) => {
10265                    self.write_keyword("AS");
10266                    self.write_space();
10267                    self.generate_expression(expr)?;
10268                }
10269                FunctionBody::External(name) => {
10270                    self.write_keyword("EXTERNAL NAME");
10271                    self.write(" '");
10272                    self.write(name);
10273                    self.write("'");
10274                }
10275                FunctionBody::Return(expr) => {
10276                    self.write_keyword("RETURN");
10277                    self.write_space();
10278                    self.generate_expression(expr)?;
10279                }
10280                FunctionBody::Statements(stmts) => {
10281                    self.write_keyword("AS");
10282                    self.write(" BEGIN ");
10283                    for (i, stmt) in stmts.iter().enumerate() {
10284                        if i > 0 {
10285                            self.write(" ");
10286                        }
10287                        self.generate_expression(stmt)?;
10288                    }
10289                    self.write(" END");
10290                }
10291                FunctionBody::DollarQuoted { content, tag } => {
10292                    self.write_keyword("AS");
10293                    self.write(" ");
10294                    // Dialects that support dollar-quoted strings: PostgreSQL, Databricks, Redshift, DuckDB
10295                    let supports_dollar_quoting = matches!(
10296                        self.config.dialect,
10297                        Some(crate::dialects::DialectType::PostgreSQL)
10298                            | Some(crate::dialects::DialectType::Databricks)
10299                            | Some(crate::dialects::DialectType::Redshift)
10300                            | Some(crate::dialects::DialectType::DuckDB)
10301                    );
10302                    if supports_dollar_quoting {
10303                        // Output in dollar-quoted format
10304                        self.write("$");
10305                        if let Some(t) = tag {
10306                            self.write(t);
10307                        }
10308                        self.write("$");
10309                        self.write(content);
10310                        self.write("$");
10311                        if let Some(t) = tag {
10312                            self.write(t);
10313                        }
10314                        self.write("$");
10315                    } else {
10316                        // Convert to single-quoted string for other dialects
10317                        let escaped = self.escape_block_for_single_quote(content);
10318                        self.write("'");
10319                        self.write(&escaped);
10320                        self.write("'");
10321                    }
10322                }
10323            }
10324        }
10325
10326        Ok(())
10327    }
10328
10329    fn generate_drop_procedure(&mut self, dp: &DropProcedure) -> Result<()> {
10330        self.write_keyword("DROP PROCEDURE");
10331
10332        if dp.if_exists {
10333            self.write_space();
10334            self.write_keyword("IF EXISTS");
10335        }
10336
10337        self.write_space();
10338        self.generate_table(&dp.name)?;
10339
10340        if let Some(params) = &dp.parameters {
10341            self.write(" (");
10342            for (i, dt) in params.iter().enumerate() {
10343                if i > 0 {
10344                    self.write(", ");
10345                }
10346                self.generate_data_type(dt)?;
10347            }
10348            self.write(")");
10349        }
10350
10351        if dp.cascade {
10352            self.write_space();
10353            self.write_keyword("CASCADE");
10354        }
10355
10356        Ok(())
10357    }
10358
10359    fn generate_create_sequence(&mut self, cs: &CreateSequence) -> Result<()> {
10360        self.write_keyword("CREATE");
10361
10362        if cs.temporary {
10363            self.write_space();
10364            self.write_keyword("TEMPORARY");
10365        }
10366
10367        self.write_space();
10368        self.write_keyword("SEQUENCE");
10369
10370        if cs.if_not_exists {
10371            self.write_space();
10372            self.write_keyword("IF NOT EXISTS");
10373        }
10374
10375        self.write_space();
10376        self.generate_table(&cs.name)?;
10377
10378        // Output COMMENT first (Snowflake convention: COMMENT comes before other properties)
10379        if let Some(comment) = &cs.comment {
10380            self.write_space();
10381            self.write_keyword("COMMENT");
10382            self.write("=");
10383            self.generate_string_literal(comment)?;
10384        }
10385
10386        // If property_order is available, use it to preserve original order
10387        if !cs.property_order.is_empty() {
10388            for prop in &cs.property_order {
10389                match prop {
10390                    SeqPropKind::Start => {
10391                        if let Some(start) = cs.start {
10392                            self.write_space();
10393                            self.write_keyword("START WITH");
10394                            self.write(&format!(" {}", start));
10395                        }
10396                    }
10397                    SeqPropKind::Increment => {
10398                        if let Some(inc) = cs.increment {
10399                            self.write_space();
10400                            self.write_keyword("INCREMENT BY");
10401                            self.write(&format!(" {}", inc));
10402                        }
10403                    }
10404                    SeqPropKind::Minvalue => {
10405                        if let Some(min) = &cs.minvalue {
10406                            self.write_space();
10407                            match min {
10408                                SequenceBound::Value(v) => {
10409                                    self.write_keyword("MINVALUE");
10410                                    self.write(&format!(" {}", v));
10411                                }
10412                                SequenceBound::None => {
10413                                    self.write_keyword("NO MINVALUE");
10414                                }
10415                            }
10416                        }
10417                    }
10418                    SeqPropKind::Maxvalue => {
10419                        if let Some(max) = &cs.maxvalue {
10420                            self.write_space();
10421                            match max {
10422                                SequenceBound::Value(v) => {
10423                                    self.write_keyword("MAXVALUE");
10424                                    self.write(&format!(" {}", v));
10425                                }
10426                                SequenceBound::None => {
10427                                    self.write_keyword("NO MAXVALUE");
10428                                }
10429                            }
10430                        }
10431                    }
10432                    SeqPropKind::Cache => {
10433                        if let Some(cache) = cs.cache {
10434                            self.write_space();
10435                            self.write_keyword("CACHE");
10436                            self.write(&format!(" {}", cache));
10437                        }
10438                    }
10439                    SeqPropKind::Cycle => {
10440                        self.write_space();
10441                        self.write_keyword("CYCLE");
10442                    }
10443                    SeqPropKind::NoCycle => {
10444                        self.write_space();
10445                        self.write_keyword("NO CYCLE");
10446                    }
10447                    SeqPropKind::OwnedBy => {
10448                        if let Some(owned) = &cs.owned_by {
10449                            self.write_space();
10450                            self.write_keyword("OWNED BY");
10451                            self.write_space();
10452                            self.generate_table(owned)?;
10453                        }
10454                    }
10455                    SeqPropKind::Order => {
10456                        self.write_space();
10457                        self.write_keyword("ORDER");
10458                    }
10459                    SeqPropKind::NoOrder => {
10460                        self.write_space();
10461                        self.write_keyword("NOORDER");
10462                    }
10463                    SeqPropKind::Comment => {
10464                        // Comment is handled above, before property_order iteration
10465                    }
10466                }
10467            }
10468        } else {
10469            // Fallback: default order for backwards compatibility
10470            if let Some(inc) = cs.increment {
10471                self.write_space();
10472                self.write_keyword("INCREMENT BY");
10473                self.write(&format!(" {}", inc));
10474            }
10475
10476            if let Some(min) = &cs.minvalue {
10477                self.write_space();
10478                match min {
10479                    SequenceBound::Value(v) => {
10480                        self.write_keyword("MINVALUE");
10481                        self.write(&format!(" {}", v));
10482                    }
10483                    SequenceBound::None => {
10484                        self.write_keyword("NO MINVALUE");
10485                    }
10486                }
10487            }
10488
10489            if let Some(max) = &cs.maxvalue {
10490                self.write_space();
10491                match max {
10492                    SequenceBound::Value(v) => {
10493                        self.write_keyword("MAXVALUE");
10494                        self.write(&format!(" {}", v));
10495                    }
10496                    SequenceBound::None => {
10497                        self.write_keyword("NO MAXVALUE");
10498                    }
10499                }
10500            }
10501
10502            if let Some(start) = cs.start {
10503                self.write_space();
10504                self.write_keyword("START WITH");
10505                self.write(&format!(" {}", start));
10506            }
10507
10508            if let Some(cache) = cs.cache {
10509                self.write_space();
10510                self.write_keyword("CACHE");
10511                self.write(&format!(" {}", cache));
10512            }
10513
10514            if cs.cycle {
10515                self.write_space();
10516                self.write_keyword("CYCLE");
10517            }
10518
10519            if let Some(owned) = &cs.owned_by {
10520                self.write_space();
10521                self.write_keyword("OWNED BY");
10522                self.write_space();
10523                self.generate_table(owned)?;
10524            }
10525        }
10526
10527        Ok(())
10528    }
10529
10530    fn generate_drop_sequence(&mut self, ds: &DropSequence) -> Result<()> {
10531        self.write_keyword("DROP SEQUENCE");
10532
10533        if ds.if_exists {
10534            self.write_space();
10535            self.write_keyword("IF EXISTS");
10536        }
10537
10538        self.write_space();
10539        self.generate_table(&ds.name)?;
10540
10541        if ds.cascade {
10542            self.write_space();
10543            self.write_keyword("CASCADE");
10544        }
10545
10546        Ok(())
10547    }
10548
10549    fn generate_alter_sequence(&mut self, als: &AlterSequence) -> Result<()> {
10550        self.write_keyword("ALTER SEQUENCE");
10551
10552        if als.if_exists {
10553            self.write_space();
10554            self.write_keyword("IF EXISTS");
10555        }
10556
10557        self.write_space();
10558        self.generate_table(&als.name)?;
10559
10560        if let Some(inc) = als.increment {
10561            self.write_space();
10562            self.write_keyword("INCREMENT BY");
10563            self.write(&format!(" {}", inc));
10564        }
10565
10566        if let Some(min) = &als.minvalue {
10567            self.write_space();
10568            match min {
10569                SequenceBound::Value(v) => {
10570                    self.write_keyword("MINVALUE");
10571                    self.write(&format!(" {}", v));
10572                }
10573                SequenceBound::None => {
10574                    self.write_keyword("NO MINVALUE");
10575                }
10576            }
10577        }
10578
10579        if let Some(max) = &als.maxvalue {
10580            self.write_space();
10581            match max {
10582                SequenceBound::Value(v) => {
10583                    self.write_keyword("MAXVALUE");
10584                    self.write(&format!(" {}", v));
10585                }
10586                SequenceBound::None => {
10587                    self.write_keyword("NO MAXVALUE");
10588                }
10589            }
10590        }
10591
10592        if let Some(start) = als.start {
10593            self.write_space();
10594            self.write_keyword("START WITH");
10595            self.write(&format!(" {}", start));
10596        }
10597
10598        if let Some(restart) = &als.restart {
10599            self.write_space();
10600            self.write_keyword("RESTART");
10601            if let Some(val) = restart {
10602                self.write_keyword(" WITH");
10603                self.write(&format!(" {}", val));
10604            }
10605        }
10606
10607        if let Some(cache) = als.cache {
10608            self.write_space();
10609            self.write_keyword("CACHE");
10610            self.write(&format!(" {}", cache));
10611        }
10612
10613        if let Some(cycle) = als.cycle {
10614            self.write_space();
10615            if cycle {
10616                self.write_keyword("CYCLE");
10617            } else {
10618                self.write_keyword("NO CYCLE");
10619            }
10620        }
10621
10622        if let Some(owned) = &als.owned_by {
10623            self.write_space();
10624            self.write_keyword("OWNED BY");
10625            self.write_space();
10626            if let Some(table) = owned {
10627                self.generate_table(table)?;
10628            } else {
10629                self.write_keyword("NONE");
10630            }
10631        }
10632
10633        Ok(())
10634    }
10635
10636    fn generate_create_trigger(&mut self, ct: &CreateTrigger) -> Result<()> {
10637        self.write_keyword("CREATE");
10638
10639        if ct.or_replace {
10640            self.write_space();
10641            self.write_keyword("OR REPLACE");
10642        }
10643
10644        if ct.constraint {
10645            self.write_space();
10646            self.write_keyword("CONSTRAINT");
10647        }
10648
10649        self.write_space();
10650        self.write_keyword("TRIGGER");
10651        self.write_space();
10652        self.generate_identifier(&ct.name)?;
10653
10654        self.write_space();
10655        match ct.timing {
10656            TriggerTiming::Before => self.write_keyword("BEFORE"),
10657            TriggerTiming::After => self.write_keyword("AFTER"),
10658            TriggerTiming::InsteadOf => self.write_keyword("INSTEAD OF"),
10659        }
10660
10661        // Events
10662        for (i, event) in ct.events.iter().enumerate() {
10663            if i > 0 {
10664                self.write_keyword(" OR");
10665            }
10666            self.write_space();
10667            match event {
10668                TriggerEvent::Insert => self.write_keyword("INSERT"),
10669                TriggerEvent::Update(cols) => {
10670                    self.write_keyword("UPDATE");
10671                    if let Some(cols) = cols {
10672                        self.write_space();
10673                        self.write_keyword("OF");
10674                        for (j, col) in cols.iter().enumerate() {
10675                            if j > 0 {
10676                                self.write(",");
10677                            }
10678                            self.write_space();
10679                            self.generate_identifier(col)?;
10680                        }
10681                    }
10682                }
10683                TriggerEvent::Delete => self.write_keyword("DELETE"),
10684                TriggerEvent::Truncate => self.write_keyword("TRUNCATE"),
10685            }
10686        }
10687
10688        self.write_space();
10689        self.write_keyword("ON");
10690        self.write_space();
10691        self.generate_table(&ct.table)?;
10692
10693        // Referencing clause
10694        if let Some(ref_clause) = &ct.referencing {
10695            self.write_space();
10696            self.write_keyword("REFERENCING");
10697            if let Some(old_table) = &ref_clause.old_table {
10698                self.write_space();
10699                self.write_keyword("OLD TABLE AS");
10700                self.write_space();
10701                self.generate_identifier(old_table)?;
10702            }
10703            if let Some(new_table) = &ref_clause.new_table {
10704                self.write_space();
10705                self.write_keyword("NEW TABLE AS");
10706                self.write_space();
10707                self.generate_identifier(new_table)?;
10708            }
10709            if let Some(old_row) = &ref_clause.old_row {
10710                self.write_space();
10711                self.write_keyword("OLD ROW AS");
10712                self.write_space();
10713                self.generate_identifier(old_row)?;
10714            }
10715            if let Some(new_row) = &ref_clause.new_row {
10716                self.write_space();
10717                self.write_keyword("NEW ROW AS");
10718                self.write_space();
10719                self.generate_identifier(new_row)?;
10720            }
10721        }
10722
10723        // Deferrable options for constraint triggers (must come before FOR EACH)
10724        if let Some(deferrable) = ct.deferrable {
10725            self.write_space();
10726            if deferrable {
10727                self.write_keyword("DEFERRABLE");
10728            } else {
10729                self.write_keyword("NOT DEFERRABLE");
10730            }
10731        }
10732
10733        if let Some(initially) = ct.initially_deferred {
10734            self.write_space();
10735            self.write_keyword("INITIALLY");
10736            self.write_space();
10737            if initially {
10738                self.write_keyword("DEFERRED");
10739            } else {
10740                self.write_keyword("IMMEDIATE");
10741            }
10742        }
10743
10744        self.write_space();
10745        self.write_keyword("FOR EACH");
10746        self.write_space();
10747        match ct.for_each {
10748            TriggerForEach::Row => self.write_keyword("ROW"),
10749            TriggerForEach::Statement => self.write_keyword("STATEMENT"),
10750        }
10751
10752        // When clause
10753        if let Some(when) = &ct.when {
10754            self.write_space();
10755            self.write_keyword("WHEN");
10756            self.write(" (");
10757            self.generate_expression(when)?;
10758            self.write(")");
10759        }
10760
10761        // Body
10762        self.write_space();
10763        match &ct.body {
10764            TriggerBody::Execute { function, args } => {
10765                self.write_keyword("EXECUTE FUNCTION");
10766                self.write_space();
10767                self.generate_table(function)?;
10768                self.write("(");
10769                for (i, arg) in args.iter().enumerate() {
10770                    if i > 0 {
10771                        self.write(", ");
10772                    }
10773                    self.generate_expression(arg)?;
10774                }
10775                self.write(")");
10776            }
10777            TriggerBody::Block(block) => {
10778                self.write_keyword("BEGIN");
10779                self.write_space();
10780                self.write(block);
10781                self.write_space();
10782                self.write_keyword("END");
10783            }
10784        }
10785
10786        Ok(())
10787    }
10788
10789    fn generate_drop_trigger(&mut self, dt: &DropTrigger) -> Result<()> {
10790        self.write_keyword("DROP TRIGGER");
10791
10792        if dt.if_exists {
10793            self.write_space();
10794            self.write_keyword("IF EXISTS");
10795        }
10796
10797        self.write_space();
10798        self.generate_identifier(&dt.name)?;
10799
10800        if let Some(table) = &dt.table {
10801            self.write_space();
10802            self.write_keyword("ON");
10803            self.write_space();
10804            self.generate_table(table)?;
10805        }
10806
10807        if dt.cascade {
10808            self.write_space();
10809            self.write_keyword("CASCADE");
10810        }
10811
10812        Ok(())
10813    }
10814
10815    fn generate_create_type(&mut self, ct: &CreateType) -> Result<()> {
10816        self.write_keyword("CREATE TYPE");
10817
10818        if ct.if_not_exists {
10819            self.write_space();
10820            self.write_keyword("IF NOT EXISTS");
10821        }
10822
10823        self.write_space();
10824        self.generate_table(&ct.name)?;
10825
10826        self.write_space();
10827        self.write_keyword("AS");
10828        self.write_space();
10829
10830        match &ct.definition {
10831            TypeDefinition::Enum(values) => {
10832                self.write_keyword("ENUM");
10833                self.write(" (");
10834                for (i, val) in values.iter().enumerate() {
10835                    if i > 0 {
10836                        self.write(", ");
10837                    }
10838                    self.write(&format!("'{}'", val));
10839                }
10840                self.write(")");
10841            }
10842            TypeDefinition::Composite(attrs) => {
10843                self.write("(");
10844                for (i, attr) in attrs.iter().enumerate() {
10845                    if i > 0 {
10846                        self.write(", ");
10847                    }
10848                    self.generate_identifier(&attr.name)?;
10849                    self.write_space();
10850                    self.generate_data_type(&attr.data_type)?;
10851                    if let Some(collate) = &attr.collate {
10852                        self.write_space();
10853                        self.write_keyword("COLLATE");
10854                        self.write_space();
10855                        self.generate_identifier(collate)?;
10856                    }
10857                }
10858                self.write(")");
10859            }
10860            TypeDefinition::Range { subtype, subtype_diff, canonical } => {
10861                self.write_keyword("RANGE");
10862                self.write(" (");
10863                self.write_keyword("SUBTYPE");
10864                self.write(" = ");
10865                self.generate_data_type(subtype)?;
10866                if let Some(diff) = subtype_diff {
10867                    self.write(", ");
10868                    self.write_keyword("SUBTYPE_DIFF");
10869                    self.write(" = ");
10870                    self.write(diff);
10871                }
10872                if let Some(canon) = canonical {
10873                    self.write(", ");
10874                    self.write_keyword("CANONICAL");
10875                    self.write(" = ");
10876                    self.write(canon);
10877                }
10878                self.write(")");
10879            }
10880            TypeDefinition::Base { input, output, internallength } => {
10881                self.write("(");
10882                self.write_keyword("INPUT");
10883                self.write(" = ");
10884                self.write(input);
10885                self.write(", ");
10886                self.write_keyword("OUTPUT");
10887                self.write(" = ");
10888                self.write(output);
10889                if let Some(len) = internallength {
10890                    self.write(", ");
10891                    self.write_keyword("INTERNALLENGTH");
10892                    self.write(" = ");
10893                    self.write(&len.to_string());
10894                }
10895                self.write(")");
10896            }
10897            TypeDefinition::Domain { base_type, default, constraints } => {
10898                self.generate_data_type(base_type)?;
10899                if let Some(def) = default {
10900                    self.write_space();
10901                    self.write_keyword("DEFAULT");
10902                    self.write_space();
10903                    self.generate_expression(def)?;
10904                }
10905                for constr in constraints {
10906                    self.write_space();
10907                    if let Some(name) = &constr.name {
10908                        self.write_keyword("CONSTRAINT");
10909                        self.write_space();
10910                        self.generate_identifier(name)?;
10911                        self.write_space();
10912                    }
10913                    self.write_keyword("CHECK");
10914                    self.write(" (");
10915                    self.generate_expression(&constr.check)?;
10916                    self.write(")");
10917                }
10918            }
10919        }
10920
10921        Ok(())
10922    }
10923
10924    fn generate_drop_type(&mut self, dt: &DropType) -> Result<()> {
10925        self.write_keyword("DROP TYPE");
10926
10927        if dt.if_exists {
10928            self.write_space();
10929            self.write_keyword("IF EXISTS");
10930        }
10931
10932        self.write_space();
10933        self.generate_table(&dt.name)?;
10934
10935        if dt.cascade {
10936            self.write_space();
10937            self.write_keyword("CASCADE");
10938        }
10939
10940        Ok(())
10941    }
10942
10943    fn generate_describe(&mut self, d: &Describe) -> Result<()> {
10944        // Athena: DESCRIBE uses Hive engine (backticks)
10945        let saved_athena_hive_context = self.athena_hive_context;
10946        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Athena)) {
10947            self.athena_hive_context = true;
10948        }
10949
10950        // Output leading comments before DESCRIBE
10951        for comment in &d.leading_comments {
10952            self.write_formatted_comment(comment);
10953            self.write(" ");
10954        }
10955
10956        self.write_keyword("DESCRIBE");
10957
10958        if d.extended {
10959            self.write_space();
10960            self.write_keyword("EXTENDED");
10961        } else if d.formatted {
10962            self.write_space();
10963            self.write_keyword("FORMATTED");
10964        }
10965
10966        // Output style like ANALYZE, HISTORY
10967        if let Some(ref style) = d.style {
10968            self.write_space();
10969            self.write_keyword(style);
10970        }
10971
10972        // Handle object kind (TABLE, VIEW) based on dialect
10973        let should_output_kind = match self.config.dialect {
10974            // Spark doesn't use TABLE/VIEW after DESCRIBE
10975            Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => false,
10976            // Snowflake always includes TABLE
10977            Some(DialectType::Snowflake) => true,
10978            _ => d.kind.is_some(),
10979        };
10980        if should_output_kind {
10981            if let Some(ref kind) = d.kind {
10982                self.write_space();
10983                self.write_keyword(kind);
10984            } else if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
10985                self.write_space();
10986                self.write_keyword("TABLE");
10987            }
10988        }
10989
10990        self.write_space();
10991        self.generate_expression(&d.target)?;
10992
10993        // Output PARTITION clause if present (the Partition expression outputs its own PARTITION keyword)
10994        if let Some(ref partition) = d.partition {
10995            self.write_space();
10996            self.generate_expression(partition)?;
10997        }
10998
10999        // Output properties like type=stage
11000        for (name, value) in &d.properties {
11001            self.write_space();
11002            self.write(name);
11003            self.write("=");
11004            self.write(value);
11005        }
11006
11007        // Restore Athena Hive context
11008        self.athena_hive_context = saved_athena_hive_context;
11009
11010        Ok(())
11011    }
11012
11013    /// Generate SHOW statement (Snowflake, MySQL, etc.)
11014    /// SHOW [TERSE] <object_type> [HISTORY] [LIKE pattern] [IN <scope>] [STARTS WITH pattern] [LIMIT n] [FROM object]
11015    fn generate_show(&mut self, s: &Show) -> Result<()> {
11016        self.write_keyword("SHOW");
11017        self.write_space();
11018
11019        // TERSE keyword - but not for PRIMARY KEYS, UNIQUE KEYS, IMPORTED KEYS
11020        // where TERSE is syntactically valid but has no effect on output
11021        let show_terse = s.terse && !matches!(
11022            s.this.as_str(),
11023            "PRIMARY KEYS" | "UNIQUE KEYS" | "IMPORTED KEYS"
11024        );
11025        if show_terse {
11026            self.write_keyword("TERSE");
11027            self.write_space();
11028        }
11029
11030        // Object type (USERS, TABLES, DATABASES, etc.)
11031        self.write_keyword(&s.this);
11032
11033        // Target identifier (MySQL: engine name in SHOW ENGINE, preserved case)
11034        if let Some(ref target_expr) = s.target {
11035            self.write_space();
11036            self.generate_expression(target_expr)?;
11037        }
11038
11039        // HISTORY keyword
11040        if s.history {
11041            self.write_space();
11042            self.write_keyword("HISTORY");
11043        }
11044
11045        // FOR target (MySQL: SHOW GRANTS FOR foo, SHOW PROFILE ... FOR QUERY 5)
11046        if let Some(ref for_target) = s.for_target {
11047            self.write_space();
11048            self.write_keyword("FOR");
11049            self.write_space();
11050            self.generate_expression(for_target)?;
11051        }
11052
11053        // Determine ordering based on dialect:
11054        // - Snowflake: LIKE, IN, STARTS WITH, LIMIT, FROM
11055        // - MySQL: IN, FROM, LIKE (when FROM is present)
11056        use crate::dialects::DialectType;
11057        let is_snowflake = matches!(self.config.dialect, Some(DialectType::Snowflake));
11058
11059        if !is_snowflake && s.from.is_some() {
11060            // MySQL ordering: IN, FROM, LIKE
11061
11062            // IN scope_kind [scope]
11063            if let Some(ref scope_kind) = s.scope_kind {
11064                self.write_space();
11065                self.write_keyword("IN");
11066                self.write_space();
11067                self.write_keyword(scope_kind);
11068                if let Some(ref scope) = s.scope {
11069                    self.write_space();
11070                    self.generate_expression(scope)?;
11071                }
11072            } else if let Some(ref scope) = s.scope {
11073                self.write_space();
11074                self.write_keyword("IN");
11075                self.write_space();
11076                self.generate_expression(scope)?;
11077            }
11078
11079            // FROM clause
11080            if let Some(ref from) = s.from {
11081                self.write_space();
11082                self.write_keyword("FROM");
11083                self.write_space();
11084                self.generate_expression(from)?;
11085            }
11086
11087            // Second FROM clause (db name)
11088            if let Some(ref db) = s.db {
11089                self.write_space();
11090                self.write_keyword("FROM");
11091                self.write_space();
11092                self.generate_expression(db)?;
11093            }
11094
11095            // LIKE pattern
11096            if let Some(ref like) = s.like {
11097                self.write_space();
11098                self.write_keyword("LIKE");
11099                self.write_space();
11100                self.generate_expression(like)?;
11101            }
11102        } else {
11103            // Snowflake ordering: LIKE, IN, STARTS WITH, LIMIT, FROM
11104
11105            // LIKE pattern
11106            if let Some(ref like) = s.like {
11107                self.write_space();
11108                self.write_keyword("LIKE");
11109                self.write_space();
11110                self.generate_expression(like)?;
11111            }
11112
11113            // IN scope_kind [scope]
11114            if let Some(ref scope_kind) = s.scope_kind {
11115                self.write_space();
11116                self.write_keyword("IN");
11117                self.write_space();
11118                self.write_keyword(scope_kind);
11119                if let Some(ref scope) = s.scope {
11120                    self.write_space();
11121                    self.generate_expression(scope)?;
11122                }
11123            } else if let Some(ref scope) = s.scope {
11124                self.write_space();
11125                self.write_keyword("IN");
11126                self.write_space();
11127                self.generate_expression(scope)?;
11128            }
11129        }
11130
11131        // STARTS WITH pattern
11132        if let Some(ref starts_with) = s.starts_with {
11133            self.write_space();
11134            self.write_keyword("STARTS WITH");
11135            self.write_space();
11136            self.generate_expression(starts_with)?;
11137        }
11138
11139        // LIMIT clause
11140        if let Some(ref limit) = s.limit {
11141            self.write_space();
11142            self.generate_limit(limit)?;
11143        }
11144
11145        // FROM clause (for Snowflake, FROM comes after STARTS WITH and LIMIT)
11146        if is_snowflake {
11147            if let Some(ref from) = s.from {
11148                self.write_space();
11149                self.write_keyword("FROM");
11150                self.write_space();
11151                self.generate_expression(from)?;
11152            }
11153        }
11154
11155        // WHERE clause (MySQL: SHOW STATUS WHERE condition)
11156        if let Some(ref where_clause) = s.where_clause {
11157            self.write_space();
11158            self.write_keyword("WHERE");
11159            self.write_space();
11160            self.generate_expression(where_clause)?;
11161        }
11162
11163        // MUTEX/STATUS suffix (MySQL: SHOW ENGINE foo STATUS/MUTEX)
11164        if let Some(is_mutex) = s.mutex {
11165            self.write_space();
11166            if is_mutex {
11167                self.write_keyword("MUTEX");
11168            } else {
11169                self.write_keyword("STATUS");
11170            }
11171        }
11172
11173        // WITH PRIVILEGES clause (Snowflake: SHOW ... WITH PRIVILEGES USAGE, MODIFY)
11174        if !s.privileges.is_empty() {
11175            self.write_space();
11176            self.write_keyword("WITH PRIVILEGES");
11177            self.write_space();
11178            for (i, priv_name) in s.privileges.iter().enumerate() {
11179                if i > 0 {
11180                    self.write(", ");
11181                }
11182                self.write_keyword(priv_name);
11183            }
11184        }
11185
11186        Ok(())
11187    }
11188
11189    // ==================== End DDL Generation ====================
11190
11191    fn generate_literal(&mut self, lit: &Literal) -> Result<()> {
11192        use crate::dialects::DialectType;
11193        match lit {
11194            Literal::String(s) => {
11195                self.generate_string_literal(s)?;
11196            }
11197            Literal::Number(n) => {
11198                if matches!(self.config.dialect, Some(DialectType::MySQL))
11199                    && n.len() > 2
11200                    && (n.starts_with("0x") || n.starts_with("0X"))
11201                    && !n[2..].chars().all(|c| c.is_ascii_hexdigit())
11202                {
11203                    return self.generate_identifier(&Identifier {
11204                        name: n.clone(),
11205                        quoted: true,
11206                        trailing_comments: Vec::new(),
11207                    });
11208                }
11209                // Normalize numbers starting with decimal point to have leading zero
11210                // e.g., .25 -> 0.25 (matches sqlglot behavior)
11211                if n.starts_with('.') {
11212                    self.write("0");
11213                    self.write(n);
11214                } else if n.starts_with("-.") {
11215                    // Handle negative numbers like -.25 -> -0.25
11216                    self.write("-0");
11217                    self.write(&n[1..]);
11218                } else {
11219                    self.write(n);
11220                }
11221            }
11222            Literal::HexString(h) => {
11223                // Most dialects use lowercase x'...' for hex literals; Spark/Databricks/Teradata use uppercase X'...'
11224                match self.config.dialect {
11225                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Teradata) => self.write("X'"),
11226                    _ => self.write("x'"),
11227                }
11228                self.write(h);
11229                self.write("'");
11230            }
11231            Literal::HexNumber(h) => {
11232                // Hex number (0xA) - integer in hex notation (from BigQuery)
11233                // For BigQuery, output as 0xHEX
11234                // For other dialects, convert to decimal integer
11235                match self.config.dialect {
11236                    Some(DialectType::BigQuery) => {
11237                        self.write("0x");
11238                        self.write(h);
11239                    }
11240                    _ => {
11241                        // Convert hex to decimal
11242                        if let Ok(val) = u64::from_str_radix(h, 16) {
11243                            self.write(&val.to_string());
11244                        } else {
11245                            // Fallback: keep as 0x notation
11246                            self.write("0x");
11247                            self.write(h);
11248                        }
11249                    }
11250                }
11251            }
11252            Literal::BitString(b) => {
11253                // Bit string B'0101...'
11254                self.write("B'");
11255                self.write(b);
11256                self.write("'");
11257            }
11258            Literal::ByteString(b) => {
11259                // Byte string b'...' (BigQuery style)
11260                self.write("b'");
11261                // Escape special characters for output
11262                self.write_escaped_byte_string(b);
11263                self.write("'");
11264            }
11265            Literal::NationalString(s) => {
11266                // N'string' is supported by TSQL, Oracle, MySQL, and generic SQL
11267                // Other dialects strip the N prefix and output as regular string
11268                let keep_n_prefix = matches!(self.config.dialect,
11269                    Some(DialectType::TSQL) | Some(DialectType::Oracle) | Some(DialectType::MySQL) | None
11270                );
11271                if keep_n_prefix {
11272                    self.write("N'");
11273                } else {
11274                    self.write("'");
11275                }
11276                self.write(s);
11277                self.write("'");
11278            }
11279            Literal::Date(d) => {
11280                self.generate_date_literal(d)?;
11281            }
11282            Literal::Time(t) => {
11283                self.generate_time_literal(t)?;
11284            }
11285            Literal::Timestamp(ts) => {
11286                self.generate_timestamp_literal(ts)?;
11287            }
11288            Literal::Datetime(dt) => {
11289                self.generate_datetime_literal(dt)?;
11290            }
11291            Literal::TripleQuotedString(s, _quote_char) => {
11292                // For BigQuery and other dialects that don't support triple-quote, normalize to regular strings
11293                if matches!(self.config.dialect, Some(crate::dialects::DialectType::BigQuery)
11294                    | Some(crate::dialects::DialectType::DuckDB)
11295                    | Some(crate::dialects::DialectType::Snowflake)
11296                    | Some(crate::dialects::DialectType::Spark)
11297                    | Some(crate::dialects::DialectType::Hive)
11298                    | Some(crate::dialects::DialectType::Presto)
11299                    | Some(crate::dialects::DialectType::Trino)
11300                    | Some(crate::dialects::DialectType::PostgreSQL)
11301                    | Some(crate::dialects::DialectType::MySQL)
11302                    | Some(crate::dialects::DialectType::Redshift)
11303                    | Some(crate::dialects::DialectType::TSQL)
11304                    | Some(crate::dialects::DialectType::Oracle)
11305                    | Some(crate::dialects::DialectType::ClickHouse)
11306                    | Some(crate::dialects::DialectType::Databricks)
11307                    | Some(crate::dialects::DialectType::SQLite)
11308                ) {
11309                    self.generate_string_literal(s)?;
11310                } else {
11311                    // Preserve triple-quoted string syntax for generic/unknown dialects
11312                    let quotes = format!("{0}{0}{0}", _quote_char);
11313                    self.write(&quotes);
11314                    self.write(s);
11315                    self.write(&quotes);
11316                }
11317            }
11318            Literal::EscapeString(s) => {
11319                // PostgreSQL escape string: e'...' or E'...'
11320                // Token text format is "e:content" or "E:content"
11321                // Normalize escape sequences: \' -> '' (standard SQL doubled quote)
11322                use crate::dialects::DialectType;
11323                let content = if let Some(c) = s.strip_prefix("e:") {
11324                    c
11325                } else if let Some(c) = s.strip_prefix("E:") {
11326                    c
11327                } else {
11328                    s.as_str()
11329                };
11330
11331                // MySQL: output the content without quotes or prefix
11332                if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::TiDB)) {
11333                    self.write(content);
11334                } else {
11335                    // Some dialects use lowercase e' prefix
11336                    let prefix = if matches!(
11337                        self.config.dialect,
11338                        Some(DialectType::SingleStore)
11339                            | Some(DialectType::DuckDB)
11340                            | Some(DialectType::PostgreSQL)
11341                            | Some(DialectType::CockroachDB)
11342                            | Some(DialectType::Materialize)
11343                            | Some(DialectType::RisingWave)
11344                    ) {
11345                        "e'"
11346                    } else {
11347                        "E'"
11348                    };
11349
11350                    // Normalize \' to '' for output
11351                    let normalized = content.replace("\\'", "''");
11352                    self.write(prefix);
11353                    self.write(&normalized);
11354                    self.write("'");
11355                }
11356            }
11357            Literal::DollarString(s) => {
11358                // Convert dollar-quoted strings to single-quoted strings
11359                // (like Python sqlglot's rawstring_sql)
11360                use crate::dialects::DialectType;
11361                // Extract content from tag\x00content format
11362                let (_tag, content) = crate::tokens::parse_dollar_string_token(s);
11363                // Step 1: Escape backslashes if the dialect uses backslash as a string escape
11364                let escape_backslash = matches!(
11365                    self.config.dialect,
11366                    Some(DialectType::Snowflake)
11367                );
11368                // Step 2: Determine quote escaping style
11369                // Snowflake: ' -> \' (backslash escape)
11370                // PostgreSQL, DuckDB, others: ' -> '' (doubled quote)
11371                let use_backslash_quote = matches!(
11372                    self.config.dialect,
11373                    Some(DialectType::Snowflake)
11374                );
11375
11376                let mut escaped = String::with_capacity(content.len() + 4);
11377                for ch in content.chars() {
11378                    if escape_backslash && ch == '\\' {
11379                        // Escape backslash first (before quote escaping)
11380                        escaped.push('\\');
11381                        escaped.push('\\');
11382                    } else if ch == '\'' {
11383                        if use_backslash_quote {
11384                            escaped.push('\\');
11385                            escaped.push('\'');
11386                        } else {
11387                            escaped.push('\'');
11388                            escaped.push('\'');
11389                        }
11390                    } else {
11391                        escaped.push(ch);
11392                    }
11393                }
11394                self.write("'");
11395                self.write(&escaped);
11396                self.write("'");
11397            }
11398            Literal::RawString(s) => {
11399                // Raw strings (r"..." or r'...') contain literal backslashes.
11400                // When converting to a regular string, this follows Python sqlglot's rawstring_sql:
11401                // 1. If \\ is in STRING_ESCAPES, double all backslashes
11402                // 2. Apply ESCAPED_SEQUENCES for special chars (but NOT for backslash itself)
11403                // 3. Escape quotes using STRING_ESCAPES[0] + quote_char
11404                use crate::dialects::DialectType;
11405
11406                // Dialects where \\ is in STRING_ESCAPES (backslashes need doubling)
11407                let escape_backslash = matches!(
11408                    self.config.dialect,
11409                    Some(DialectType::BigQuery)
11410                        | Some(DialectType::MySQL)
11411                        | Some(DialectType::SingleStore)
11412                        | Some(DialectType::TiDB)
11413                        | Some(DialectType::Hive)
11414                        | Some(DialectType::Spark)
11415                       
11416                        | Some(DialectType::Databricks)
11417                        | Some(DialectType::Drill)
11418                        | Some(DialectType::Snowflake)
11419                        | Some(DialectType::Redshift)
11420                        | Some(DialectType::ClickHouse)
11421                );
11422
11423                // Dialects where backslash is the PRIMARY string escape (STRING_ESCAPES[0] = "\\")
11424                // These escape quotes as \' instead of ''
11425                let backslash_escapes_quote = matches!(
11426                    self.config.dialect,
11427                    Some(DialectType::BigQuery)
11428                        | Some(DialectType::Hive)
11429                        | Some(DialectType::Spark)
11430                       
11431                        | Some(DialectType::Databricks)
11432                        | Some(DialectType::Drill)
11433                        | Some(DialectType::Snowflake)
11434                        | Some(DialectType::Redshift)
11435                );
11436
11437                // Whether this dialect supports escaped sequences (ESCAPED_SEQUENCES mapping)
11438                // This is True when \\ is in STRING_ESCAPES (same as escape_backslash)
11439                let supports_escape_sequences = escape_backslash;
11440
11441                let mut escaped = String::with_capacity(s.len() + 4);
11442                for ch in s.chars() {
11443                    if escape_backslash && ch == '\\' {
11444                        // Double the backslash for the target dialect
11445                        escaped.push('\\');
11446                        escaped.push('\\');
11447                    } else if ch == '\'' {
11448                        if backslash_escapes_quote {
11449                            // Use backslash to escape the quote: \'
11450                            escaped.push('\\');
11451                            escaped.push('\'');
11452                        } else {
11453                            // Use SQL standard quote doubling: ''
11454                            escaped.push('\'');
11455                            escaped.push('\'');
11456                        }
11457                    } else if supports_escape_sequences {
11458                        // Apply ESCAPED_SEQUENCES mapping for special chars
11459                        // (escape_backslash=False in rawstring_sql, so \\ is NOT escaped here)
11460                        match ch {
11461                            '\n' => { escaped.push('\\'); escaped.push('n'); }
11462                            '\r' => { escaped.push('\\'); escaped.push('r'); }
11463                            '\t' => { escaped.push('\\'); escaped.push('t'); }
11464                            '\x07' => { escaped.push('\\'); escaped.push('a'); }
11465                            '\x08' => { escaped.push('\\'); escaped.push('b'); }
11466                            '\x0C' => { escaped.push('\\'); escaped.push('f'); }
11467                            '\x0B' => { escaped.push('\\'); escaped.push('v'); }
11468                            _ => escaped.push(ch),
11469                        }
11470                    } else {
11471                        escaped.push(ch);
11472                    }
11473                }
11474                self.write("'");
11475                self.write(&escaped);
11476                self.write("'");
11477            }
11478        }
11479        Ok(())
11480    }
11481
11482    /// Generate a DATE literal with dialect-specific formatting
11483    fn generate_date_literal(&mut self, d: &str) -> Result<()> {
11484        use crate::dialects::DialectType;
11485
11486        match self.config.dialect {
11487            // SQL Server uses CONVERT or CAST
11488            Some(DialectType::TSQL) => {
11489                self.write("CAST('");
11490                self.write(d);
11491                self.write("' AS DATE)");
11492            }
11493            // BigQuery uses CAST syntax for type literals
11494            // DATE 'value' -> CAST('value' AS DATE)
11495            Some(DialectType::BigQuery) => {
11496                self.write("CAST('");
11497                self.write(d);
11498                self.write("' AS DATE)");
11499            }
11500            // Exasol uses CAST syntax for DATE literals
11501            // DATE 'value' -> CAST('value' AS DATE)
11502            Some(DialectType::Exasol) => {
11503                self.write("CAST('");
11504                self.write(d);
11505                self.write("' AS DATE)");
11506            }
11507            // Snowflake uses CAST syntax for DATE literals
11508            // DATE 'value' -> CAST('value' AS DATE)
11509            Some(DialectType::Snowflake) => {
11510                self.write("CAST('");
11511                self.write(d);
11512                self.write("' AS DATE)");
11513            }
11514            // PostgreSQL, MySQL, Redshift: DATE 'value' -> CAST('value' AS DATE)
11515            Some(DialectType::PostgreSQL) | Some(DialectType::MySQL)
11516            | Some(DialectType::SingleStore) | Some(DialectType::TiDB)
11517            | Some(DialectType::Redshift) => {
11518                self.write("CAST('");
11519                self.write(d);
11520                self.write("' AS DATE)");
11521            }
11522            // DuckDB, Presto, Trino, Spark: DATE 'value' -> CAST('value' AS DATE)
11523            Some(DialectType::DuckDB) | Some(DialectType::Presto) | Some(DialectType::Trino)
11524            | Some(DialectType::Athena) | Some(DialectType::Spark)
11525            | Some(DialectType::Databricks) | Some(DialectType::Hive) => {
11526                self.write("CAST('");
11527                self.write(d);
11528                self.write("' AS DATE)");
11529            }
11530            // Oracle: DATE 'value' -> TO_DATE('value', 'YYYY-MM-DD')
11531            Some(DialectType::Oracle) => {
11532                self.write("TO_DATE('");
11533                self.write(d);
11534                self.write("', 'YYYY-MM-DD')");
11535            }
11536            // Standard SQL: DATE '...'
11537            _ => {
11538                self.write_keyword("DATE");
11539                self.write(" '");
11540                self.write(d);
11541                self.write("'");
11542            }
11543        }
11544        Ok(())
11545    }
11546
11547    /// Generate a TIME literal with dialect-specific formatting
11548    fn generate_time_literal(&mut self, t: &str) -> Result<()> {
11549        use crate::dialects::DialectType;
11550
11551        match self.config.dialect {
11552            // SQL Server uses CONVERT or CAST
11553            Some(DialectType::TSQL) => {
11554                self.write("CAST('");
11555                self.write(t);
11556                self.write("' AS TIME)");
11557            }
11558            // Standard SQL: TIME '...'
11559            _ => {
11560                self.write_keyword("TIME");
11561                self.write(" '");
11562                self.write(t);
11563                self.write("'");
11564            }
11565        }
11566        Ok(())
11567    }
11568
11569    /// Generate a date expression for Dremio, converting DATE literals to CAST
11570    fn generate_dremio_date_expression(&mut self, expr: &Expression) -> Result<()> {
11571        use crate::expressions::Literal;
11572
11573        match expr {
11574            Expression::Literal(Literal::Date(d)) => {
11575                // DATE 'value' -> CAST('value' AS DATE)
11576                self.write("CAST('");
11577                self.write(d);
11578                self.write("' AS DATE)");
11579            }
11580            _ => {
11581                // For all other expressions, generate normally
11582                self.generate_expression(expr)?;
11583            }
11584        }
11585        Ok(())
11586    }
11587
11588    /// Generate a TIMESTAMP literal with dialect-specific formatting
11589    fn generate_timestamp_literal(&mut self, ts: &str) -> Result<()> {
11590        use crate::dialects::DialectType;
11591
11592        match self.config.dialect {
11593            // SQL Server uses CONVERT or CAST
11594            Some(DialectType::TSQL) => {
11595                self.write("CAST('");
11596                self.write(ts);
11597                self.write("' AS DATETIME2)");
11598            }
11599            // BigQuery uses CAST syntax for type literals
11600            // TIMESTAMP 'value' -> CAST('value' AS TIMESTAMP)
11601            Some(DialectType::BigQuery) => {
11602                self.write("CAST('");
11603                self.write(ts);
11604                self.write("' AS TIMESTAMP)");
11605            }
11606            // Snowflake uses CAST syntax for TIMESTAMP literals
11607            // TIMESTAMP 'value' -> CAST('value' AS TIMESTAMP)
11608            Some(DialectType::Snowflake) => {
11609                self.write("CAST('");
11610                self.write(ts);
11611                self.write("' AS TIMESTAMP)");
11612            }
11613            // Dremio uses CAST syntax for TIMESTAMP literals
11614            // TIMESTAMP 'value' -> CAST('value' AS TIMESTAMP)
11615            Some(DialectType::Dremio) => {
11616                self.write("CAST('");
11617                self.write(ts);
11618                self.write("' AS TIMESTAMP)");
11619            }
11620            // Exasol uses CAST syntax for TIMESTAMP literals
11621            // TIMESTAMP 'value' -> CAST('value' AS TIMESTAMP)
11622            Some(DialectType::Exasol) => {
11623                self.write("CAST('");
11624                self.write(ts);
11625                self.write("' AS TIMESTAMP)");
11626            }
11627            // Oracle prefers TO_TIMESTAMP function call
11628            // TIMESTAMP 'value' -> TO_TIMESTAMP('value', 'YYYY-MM-DD HH24:MI:SS.FF6')
11629            Some(DialectType::Oracle) => {
11630                self.write("TO_TIMESTAMP('");
11631                self.write(ts);
11632                self.write("', 'YYYY-MM-DD HH24:MI:SS.FF6')");
11633            }
11634            // Presto/Trino: always use CAST for TIMESTAMP literals
11635            Some(DialectType::Presto) | Some(DialectType::Trino) => {
11636                if Self::timestamp_has_timezone(ts) {
11637                    self.write("CAST('");
11638                    self.write(ts);
11639                    self.write("' AS TIMESTAMP WITH TIME ZONE)");
11640                } else {
11641                    self.write("CAST('");
11642                    self.write(ts);
11643                    self.write("' AS TIMESTAMP)");
11644                }
11645            }
11646            // ClickHouse: CAST('...' AS Nullable(DateTime))
11647            Some(DialectType::ClickHouse) => {
11648                self.write("CAST('");
11649                self.write(ts);
11650                self.write("' AS Nullable(DateTime))");
11651            }
11652            // Spark: CAST('...' AS TIMESTAMP)
11653            Some(DialectType::Spark) => {
11654                self.write("CAST('");
11655                self.write(ts);
11656                self.write("' AS TIMESTAMP)");
11657            }
11658            // Redshift: CAST('...' AS TIMESTAMP) for regular timestamps,
11659            // but TIMESTAMP '...' for special values like 'epoch'
11660            Some(DialectType::Redshift) => {
11661                if ts == "epoch" {
11662                    self.write_keyword("TIMESTAMP");
11663                    self.write(" '");
11664                    self.write(ts);
11665                    self.write("'");
11666                } else {
11667                    self.write("CAST('");
11668                    self.write(ts);
11669                    self.write("' AS TIMESTAMP)");
11670                }
11671            }
11672            // PostgreSQL, Hive, DuckDB, etc.: CAST('...' AS TIMESTAMP)
11673            Some(DialectType::PostgreSQL) | Some(DialectType::Hive) |
11674            Some(DialectType::SQLite) | Some(DialectType::DuckDB) |
11675            Some(DialectType::Athena) | Some(DialectType::Drill) |
11676            Some(DialectType::Teradata) => {
11677                self.write("CAST('");
11678                self.write(ts);
11679                self.write("' AS TIMESTAMP)");
11680            }
11681            // MySQL/StarRocks: CAST('...' AS DATETIME)
11682            Some(DialectType::MySQL) | Some(DialectType::StarRocks) | Some(DialectType::Doris) => {
11683                self.write("CAST('");
11684                self.write(ts);
11685                self.write("' AS DATETIME)");
11686            }
11687            // Databricks: CAST('...' AS TIMESTAMP_NTZ)
11688            Some(DialectType::Databricks) => {
11689                self.write("CAST('");
11690                self.write(ts);
11691                self.write("' AS TIMESTAMP_NTZ)");
11692            }
11693            // Standard SQL: TIMESTAMP '...'
11694            _ => {
11695                self.write_keyword("TIMESTAMP");
11696                self.write(" '");
11697                self.write(ts);
11698                self.write("'");
11699            }
11700        }
11701        Ok(())
11702    }
11703
11704    /// Check if a timestamp string contains a timezone identifier
11705    /// This detects IANA timezone names like Europe/Prague, America/New_York, etc.
11706    fn timestamp_has_timezone(ts: &str) -> bool {
11707        // Check for common IANA timezone patterns: Continent/City format
11708        // Examples: Europe/Prague, America/New_York, Asia/Tokyo, etc.
11709        // Also handles: UTC, GMT, Etc/GMT+0, etc.
11710        let ts_lower = ts.to_lowercase();
11711
11712        // Check for Continent/City pattern (most common)
11713        let continent_prefixes = [
11714            "africa/", "america/", "antarctica/", "arctic/", "asia/",
11715            "atlantic/", "australia/", "europe/", "indian/", "pacific/",
11716            "etc/", "brazil/", "canada/", "chile/", "mexico/", "us/",
11717        ];
11718
11719        for prefix in &continent_prefixes {
11720            if ts_lower.contains(prefix) {
11721                return true;
11722            }
11723        }
11724
11725        // Check for standalone timezone abbreviations at the end
11726        // These typically appear after the time portion
11727        let tz_abbrevs = [
11728            " utc", " gmt", " cet", " cest", " eet", " eest", " wet", " west",
11729            " est", " edt", " cst", " cdt", " mst", " mdt", " pst", " pdt",
11730            " ist", " bst", " jst", " kst", " hkt", " sgt", " aest", " aedt",
11731            " acst", " acdt", " awst",
11732        ];
11733
11734        for abbrev in &tz_abbrevs {
11735            if ts_lower.ends_with(abbrev) {
11736                return true;
11737            }
11738        }
11739
11740        // Check for numeric timezone offsets: +N, -N, +NN:NN, -NN:NN
11741        // Examples: "2012-10-31 01:00 -2", "2012-10-31 01:00 +02:00"
11742        // Look for pattern: space followed by + or - and digits (optionally with :)
11743        let trimmed = ts.trim();
11744        if let Some(last_space) = trimmed.rfind(' ') {
11745            let suffix = &trimmed[last_space + 1..];
11746            if (suffix.starts_with('+') || suffix.starts_with('-')) && suffix.len() > 1 {
11747                // Check if rest is numeric (possibly with : for hh:mm format)
11748                let rest = &suffix[1..];
11749                if rest.chars().all(|c| c.is_ascii_digit() || c == ':') {
11750                    return true;
11751                }
11752            }
11753        }
11754
11755        false
11756    }
11757
11758    /// Generate a DATETIME literal with dialect-specific formatting
11759    fn generate_datetime_literal(&mut self, dt: &str) -> Result<()> {
11760        use crate::dialects::DialectType;
11761
11762        match self.config.dialect {
11763            // BigQuery uses CAST syntax for type literals
11764            // DATETIME 'value' -> CAST('value' AS DATETIME)
11765            Some(DialectType::BigQuery) => {
11766                self.write("CAST('");
11767                self.write(dt);
11768                self.write("' AS DATETIME)");
11769            }
11770            // DuckDB: DATETIME -> CAST('value' AS TIMESTAMP)
11771            Some(DialectType::DuckDB) => {
11772                self.write("CAST('");
11773                self.write(dt);
11774                self.write("' AS TIMESTAMP)");
11775            }
11776            // DATETIME is primarily a BigQuery type
11777            // Output as DATETIME '...' for dialects that support it
11778            _ => {
11779                self.write_keyword("DATETIME");
11780                self.write(" '");
11781                self.write(dt);
11782                self.write("'");
11783            }
11784        }
11785        Ok(())
11786    }
11787
11788    /// Generate a string literal with dialect-specific escaping
11789    fn generate_string_literal(&mut self, s: &str) -> Result<()> {
11790        use crate::dialects::DialectType;
11791
11792        match self.config.dialect {
11793            // MySQL/Hive: Uses SQL standard quote escaping ('') for quotes,
11794            // and backslash escaping for special characters like newlines
11795            // Hive STRING_ESCAPES = ["\\"] - uses backslash escapes
11796            Some(DialectType::Hive)
11797            | Some(DialectType::Spark)
11798           
11799            | Some(DialectType::Databricks)
11800            | Some(DialectType::Drill) => {
11801                // Hive/Spark use backslash escaping for quotes (\') and special chars
11802                self.write("'");
11803                for c in s.chars() {
11804                    match c {
11805                        '\'' => self.write("\\'"),
11806                        '\\' => self.write("\\\\"),
11807                        '\n' => self.write("\\n"),
11808                        '\r' => self.write("\\r"),
11809                        '\t' => self.write("\\t"),
11810                        '\0' => self.write("\\0"),
11811                        _ => self.output.push(c),
11812                    }
11813                }
11814                self.write("'");
11815            }
11816            Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB) => {
11817                self.write("'");
11818                for c in s.chars() {
11819                    match c {
11820                        // MySQL uses SQL standard quote doubling
11821                        '\'' => self.write("''"),
11822                        '\\' => self.write("\\\\"),
11823                        '\n' => self.write("\\n"),
11824                        '\r' => self.write("\\r"),
11825                        '\t' => self.write("\\t"),
11826                        // sqlglot writes a literal NUL for this case
11827                        '\0' => self.output.push('\0'),
11828                        _ => self.output.push(c),
11829                    }
11830                }
11831                self.write("'");
11832            }
11833            // BigQuery: Uses backslash escaping
11834            Some(DialectType::BigQuery) => {
11835                self.write("'");
11836                for c in s.chars() {
11837                    match c {
11838                        '\'' => self.write("\\'"),
11839                        '\\' => self.write("\\\\"),
11840                        '\n' => self.write("\\n"),
11841                        '\r' => self.write("\\r"),
11842                        '\t' => self.write("\\t"),
11843                        '\0' => self.write("\\0"),
11844                        '\x07' => self.write("\\a"),
11845                        '\x08' => self.write("\\b"),
11846                        '\x0C' => self.write("\\f"),
11847                        '\x0B' => self.write("\\v"),
11848                        _ => self.output.push(c),
11849                    }
11850                }
11851                self.write("'");
11852            }
11853            // Athena: Uses different escaping for DDL (Hive) vs DML (Trino)
11854            // In Hive context (DDL): backslash escaping for single quotes (\') and backslashes (\\)
11855            // In Trino context (DML): SQL-standard escaping ('') and literal backslashes
11856            Some(DialectType::Athena) => {
11857                if self.athena_hive_context {
11858                    // Hive-style: backslash escaping
11859                    self.write("'");
11860                    for c in s.chars() {
11861                        match c {
11862                            '\'' => self.write("\\'"),
11863                            '\\' => self.write("\\\\"),
11864                            '\n' => self.write("\\n"),
11865                            '\r' => self.write("\\r"),
11866                            '\t' => self.write("\\t"),
11867                            '\0' => self.write("\\0"),
11868                            _ => self.output.push(c),
11869                        }
11870                    }
11871                    self.write("'");
11872                } else {
11873                    // Trino-style: SQL-standard escaping, preserve backslashes
11874                    self.write("'");
11875                    for c in s.chars() {
11876                        match c {
11877                            '\'' => self.write("''"),
11878                            // Preserve backslashes literally (no re-escaping)
11879                            _ => self.output.push(c),
11880                        }
11881                    }
11882                    self.write("'");
11883                }
11884            }
11885            // Snowflake: Uses backslash escaping (STRING_ESCAPES = ["\\", "'"])
11886            // The tokenizer preserves backslash escape sequences literally (e.g., input '\\'
11887            // becomes string value '\\'), so we should NOT re-escape backslashes.
11888            // We only need to escape single quotes.
11889            Some(DialectType::Snowflake) => {
11890                self.write("'");
11891                for c in s.chars() {
11892                    match c {
11893                        '\'' => self.write("\\'"),
11894                        // Backslashes are already escaped in the tokenized string, don't re-escape
11895                        // Only escape special characters that might not have been escaped
11896                        '\n' => self.write("\\n"),
11897                        '\r' => self.write("\\r"),
11898                        '\t' => self.write("\\t"),
11899                        _ => self.output.push(c),
11900                    }
11901                }
11902                self.write("'");
11903            }
11904            // PostgreSQL: Output special characters as literal chars in strings (no E-string prefix)
11905            Some(DialectType::PostgreSQL) => {
11906                self.write("'");
11907                for c in s.chars() {
11908                    match c {
11909                        '\'' => self.write("''"),
11910                        _ => self.output.push(c),
11911                    }
11912                }
11913                self.write("'");
11914            }
11915            // Redshift: Uses backslash escaping for single quotes
11916            Some(DialectType::Redshift) => {
11917                self.write("'");
11918                for c in s.chars() {
11919                    match c {
11920                        '\'' => self.write("\\'"),
11921                        _ => self.output.push(c),
11922                    }
11923                }
11924                self.write("'");
11925            }
11926            // Oracle: Uses standard double single-quote escaping
11927            Some(DialectType::Oracle) => {
11928                self.write("'");
11929                self.write(&s.replace('\'', "''"));
11930                self.write("'");
11931            }
11932            // ClickHouse: Uses SQL-standard quote doubling ('') for quotes,
11933            // backslash escaping for backslashes and special characters
11934            Some(DialectType::ClickHouse) => {
11935                self.write("'");
11936                for c in s.chars() {
11937                    match c {
11938                        '\'' => self.write("''"),
11939                        '\\' => self.write("\\\\"),
11940                        '\n' => self.write("\\n"),
11941                        '\r' => self.write("\\r"),
11942                        '\t' => self.write("\\t"),
11943                        '\0' => self.write("\\0"),
11944                        _ => self.output.push(c),
11945                    }
11946                }
11947                self.write("'");
11948            }
11949            // Default: SQL standard double single quotes (works for most dialects)
11950            // PostgreSQL, Snowflake, DuckDB, TSQL, etc.
11951            _ => {
11952                self.write("'");
11953                self.write(&s.replace('\'', "''"));
11954                self.write("'");
11955            }
11956        }
11957        Ok(())
11958    }
11959
11960    /// Write a byte string with proper escaping for BigQuery-style byte literals
11961    /// Escapes characters as \xNN hex escapes where needed
11962    fn write_escaped_byte_string(&mut self, s: &str) {
11963        for c in s.chars() {
11964            match c {
11965                // Escape single quotes
11966                '\'' => self.write("\\'"),
11967                // Escape backslashes
11968                '\\' => self.write("\\\\"),
11969                // Keep all printable characters (including non-ASCII) as-is
11970                _ if !c.is_control() => self.output.push(c),
11971                // Escape control characters as hex
11972                _ => {
11973                    let byte = c as u32;
11974                    if byte < 256 {
11975                        self.write(&format!("\\x{:02x}", byte));
11976                    } else {
11977                        // For unicode characters, write each UTF-8 byte
11978                        for b in c.to_string().as_bytes() {
11979                            self.write(&format!("\\x{:02x}", b));
11980                        }
11981                    }
11982                }
11983            }
11984        }
11985    }
11986
11987    fn generate_boolean(&mut self, b: &BooleanLiteral) -> Result<()> {
11988        use crate::dialects::DialectType;
11989
11990        // Different dialects have different boolean literal formats
11991        match self.config.dialect {
11992            // SQL Server typically uses 1/0 for boolean literals in many contexts
11993            // However, TRUE/FALSE also works in modern versions
11994            Some(DialectType::TSQL) => {
11995                self.write(if b.value { "1" } else { "0" });
11996            }
11997            // Oracle traditionally uses 1/0 (no native boolean until recent versions)
11998            Some(DialectType::Oracle) => {
11999                self.write(if b.value { "1" } else { "0" });
12000            }
12001            // MySQL accepts TRUE/FALSE as aliases for 1/0
12002            Some(DialectType::MySQL) => {
12003                self.write_keyword(if b.value { "TRUE" } else { "FALSE" });
12004            }
12005            // Most other dialects support TRUE/FALSE
12006            _ => {
12007                self.write_keyword(if b.value { "TRUE" } else { "FALSE" });
12008            }
12009        }
12010        Ok(())
12011    }
12012
12013    /// Generate an identifier that's used as an alias name
12014    /// This quotes reserved keywords in addition to already-quoted identifiers
12015    fn generate_alias_identifier(&mut self, id: &Identifier) -> Result<()> {
12016        let name = &id.name;
12017        let quote_style = &self.config.identifier_quote_style;
12018
12019        // For aliases, quote if:
12020        // 1. The identifier was explicitly quoted in the source
12021        // 2. The identifier is a reserved keyword for the current dialect
12022        let needs_quoting = id.quoted || self.is_reserved_keyword(name);
12023
12024        // Normalize identifier if configured
12025        let output_name = if self.config.normalize_identifiers && !id.quoted {
12026            name.to_lowercase()
12027        } else {
12028            name.to_string()
12029        };
12030
12031        if needs_quoting {
12032            // Escape any quote characters within the identifier
12033            let escaped_name = if quote_style.start == quote_style.end {
12034                output_name.replace(
12035                    quote_style.end,
12036                    &format!("{}{}", quote_style.end, quote_style.end)
12037                )
12038            } else {
12039                output_name.replace(
12040                    quote_style.end,
12041                    &format!("{}{}", quote_style.end, quote_style.end)
12042                )
12043            };
12044            self.write(&format!("{}{}{}", quote_style.start, escaped_name, quote_style.end));
12045        } else {
12046            self.write(&output_name);
12047        }
12048
12049        // Output trailing comments
12050        for comment in &id.trailing_comments {
12051            self.write(" ");
12052            self.write(comment);
12053        }
12054        Ok(())
12055    }
12056
12057    fn generate_identifier(&mut self, id: &Identifier) -> Result<()> {
12058        use crate::dialects::DialectType;
12059
12060        let name = &id.name;
12061
12062        // For Athena, use backticks in Hive context, double quotes in Trino context
12063        let quote_style = if matches!(self.config.dialect, Some(DialectType::Athena)) && self.athena_hive_context {
12064            &IdentifierQuoteStyle::BACKTICK
12065        } else {
12066            &self.config.identifier_quote_style
12067        };
12068
12069        // Quote if:
12070        // 1. The identifier was explicitly quoted in the source
12071        // 2. The identifier is a reserved keyword for the current dialect
12072        // 3. The config says to always quote identifiers (e.g., Athena/Presto)
12073        // This matches Python sqlglot's identifier_sql behavior
12074        // Also quote identifiers starting with digits if the target dialect doesn't support them
12075        let starts_with_digit = name.chars().next().map_or(false, |c| c.is_ascii_digit());
12076        let needs_digit_quoting = starts_with_digit && !self.config.identifiers_can_start_with_digit && self.config.dialect.is_some();
12077        let mysql_invalid_hex_identifier = matches!(self.config.dialect, Some(DialectType::MySQL))
12078            && name.len() > 2
12079            && (name.starts_with("0x") || name.starts_with("0X"))
12080            && !name[2..].chars().all(|c| c.is_ascii_hexdigit());
12081        let needs_quoting = id.quoted
12082            || self.is_reserved_keyword(name)
12083            || self.config.always_quote_identifiers
12084            || needs_digit_quoting
12085            || mysql_invalid_hex_identifier;
12086
12087        // Check for MySQL index column prefix length: name(16) or name(16) ASC/DESC
12088        // When quoted, we need to output `name`(16) not `name(16)`
12089        let (base_name, suffix) = if needs_quoting {
12090            // Try to extract prefix length from identifier: name(number) or name(number) ASC/DESC
12091            if let Some(paren_pos) = name.find('(') {
12092                let base = &name[..paren_pos];
12093                let rest = &name[paren_pos..];
12094                // Verify it looks like (digits) or (digits) ASC/DESC
12095                if rest.starts_with('(') && (rest.ends_with(')') || rest.ends_with(") ASC") || rest.ends_with(") DESC")) {
12096                    // Check if content between parens is all digits
12097                    let close_paren = rest.find(')').unwrap_or(rest.len());
12098                    let inside = &rest[1..close_paren];
12099                    if inside.chars().all(|c| c.is_ascii_digit()) {
12100                        (base.to_string(), rest.to_string())
12101                    } else {
12102                        (name.to_string(), String::new())
12103                    }
12104                } else {
12105                    (name.to_string(), String::new())
12106                }
12107            } else if name.ends_with(" ASC") {
12108                let base = &name[..name.len() - 4];
12109                (base.to_string(), " ASC".to_string())
12110            } else if name.ends_with(" DESC") {
12111                let base = &name[..name.len() - 5];
12112                (base.to_string(), " DESC".to_string())
12113            } else {
12114                (name.to_string(), String::new())
12115            }
12116        } else {
12117            (name.to_string(), String::new())
12118        };
12119
12120        // Normalize identifier if configured, with special handling for Exasol
12121        // Exasol uses UPPERCASE normalization strategy, so reserved keywords that need quoting
12122        // should be uppercased when not already quoted (to match Python sqlglot behavior)
12123        let output_name = if self.config.normalize_identifiers && !id.quoted {
12124            base_name.to_lowercase()
12125        } else if matches!(self.config.dialect, Some(DialectType::Exasol)) && !id.quoted && self.is_reserved_keyword(name) {
12126            // Exasol: uppercase reserved keywords when quoting them
12127            // This matches Python sqlglot's behavior with NORMALIZATION_STRATEGY = UPPERCASE
12128            base_name.to_uppercase()
12129        } else {
12130            base_name
12131        };
12132
12133        if needs_quoting {
12134            // Escape any quote characters within the identifier
12135            let escaped_name = if quote_style.start == quote_style.end {
12136                // Same start/end char (e.g., " or `) - double the quote char
12137                output_name.replace(
12138                    quote_style.end,
12139                    &format!("{}{}", quote_style.end, quote_style.end)
12140                )
12141            } else {
12142                // Different start/end (e.g., [ and ]) - escape only the end char
12143                output_name.replace(
12144                    quote_style.end,
12145                    &format!("{}{}", quote_style.end, quote_style.end)
12146                )
12147            };
12148            self.write(&format!("{}{}{}{}", quote_style.start, escaped_name, quote_style.end, suffix));
12149        } else {
12150            self.write(&output_name);
12151        }
12152
12153        // Output trailing comments
12154        for comment in &id.trailing_comments {
12155            self.write(" ");
12156            self.write(comment);
12157        }
12158        Ok(())
12159    }
12160
12161    fn generate_column(&mut self, col: &Column) -> Result<()> {
12162        use crate::dialects::DialectType;
12163
12164        if let Some(table) = &col.table {
12165            // Exasol special case: LOCAL as column table prefix should NOT be quoted
12166            // LOCAL is a special keyword in Exasol for referencing aliases from the current scope
12167            // Only applies when: dialect is Exasol, name is "LOCAL" (case-insensitive), and not already quoted
12168            let is_exasol_local_prefix = matches!(self.config.dialect, Some(DialectType::Exasol))
12169                && !table.quoted
12170                && table.name.eq_ignore_ascii_case("LOCAL");
12171
12172            if is_exasol_local_prefix {
12173                // Write LOCAL unquoted (this is special Exasol syntax, not a table reference)
12174                self.write("LOCAL");
12175            } else {
12176                self.generate_identifier(table)?;
12177            }
12178            self.write(".");
12179        }
12180        self.generate_identifier(&col.name)?;
12181        // Oracle-style join marker (+)
12182        // Only output if dialect supports it (Oracle, Exasol)
12183        if col.join_mark && self.config.supports_column_join_marks {
12184            self.write(" (+)");
12185        }
12186        // Output trailing comments
12187        for comment in &col.trailing_comments {
12188            self.write_space();
12189            self.write(comment);
12190        }
12191        Ok(())
12192    }
12193
12194    /// Generate a pseudocolumn (Oracle ROWNUM, ROWID, LEVEL, etc.)
12195    /// Pseudocolumns should NEVER be quoted, as quoting breaks them in Oracle
12196    fn generate_pseudocolumn(&mut self, pc: &Pseudocolumn) -> Result<()> {
12197        use crate::dialects::DialectType;
12198        use crate::expressions::PseudocolumnType;
12199
12200        // SYSDATE -> CURRENT_TIMESTAMP for non-Oracle/Redshift dialects
12201        if pc.kind == PseudocolumnType::Sysdate
12202            && !matches!(self.config.dialect, Some(DialectType::Oracle) | Some(DialectType::Redshift) | None)
12203        {
12204            self.write_keyword("CURRENT_TIMESTAMP");
12205            // Add () for dialects that expect it
12206            if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::ClickHouse)
12207                | Some(DialectType::Spark) | Some(DialectType::Databricks)
12208                | Some(DialectType::Hive)) {
12209                self.write("()");
12210            }
12211        } else {
12212            self.write(pc.kind.as_str());
12213        }
12214        Ok(())
12215    }
12216
12217    /// Generate CONNECT BY clause (Oracle hierarchical queries)
12218    fn generate_connect(&mut self, connect: &Connect) -> Result<()> {
12219        use crate::dialects::DialectType;
12220
12221        // Generate native CONNECT BY for Oracle and Snowflake
12222        // For other dialects, add a comment noting manual conversion needed
12223        let supports_connect_by = matches!(self.config.dialect, Some(DialectType::Oracle) | Some(DialectType::Snowflake));
12224
12225        if !supports_connect_by && self.config.dialect.is_some() {
12226            // Add comment for unsupported dialects
12227            if self.config.pretty {
12228                self.write_newline();
12229            } else {
12230                self.write_space();
12231            }
12232            self.write("/* CONNECT BY requires manual conversion to recursive CTE */");
12233        }
12234
12235        // Generate START WITH if present (before CONNECT BY)
12236        if let Some(start) = &connect.start {
12237            if self.config.pretty {
12238                self.write_newline();
12239            } else {
12240                self.write_space();
12241            }
12242            self.write_keyword("START WITH");
12243            self.write_space();
12244            self.generate_expression(start)?;
12245        }
12246
12247        // Generate CONNECT BY
12248        if self.config.pretty {
12249            self.write_newline();
12250        } else {
12251            self.write_space();
12252        }
12253        self.write_keyword("CONNECT BY");
12254        if connect.nocycle {
12255            self.write_space();
12256            self.write_keyword("NOCYCLE");
12257        }
12258        self.write_space();
12259        self.generate_expression(&connect.connect)?;
12260
12261        Ok(())
12262    }
12263
12264    /// Generate Connect expression (for Expression::Connect variant)
12265    fn generate_connect_expr(&mut self, connect: &Connect) -> Result<()> {
12266        self.generate_connect(connect)
12267    }
12268
12269    /// Generate PRIOR expression
12270    fn generate_prior(&mut self, prior: &Prior) -> Result<()> {
12271        self.write_keyword("PRIOR");
12272        self.write_space();
12273        self.generate_expression(&prior.this)?;
12274        Ok(())
12275    }
12276
12277    /// Generate CONNECT_BY_ROOT function
12278    /// Syntax: CONNECT_BY_ROOT column (no parentheses)
12279    fn generate_connect_by_root(&mut self, cbr: &ConnectByRoot) -> Result<()> {
12280        self.write_keyword("CONNECT_BY_ROOT");
12281        self.write_space();
12282        self.generate_expression(&cbr.this)?;
12283        Ok(())
12284    }
12285
12286    /// Generate MATCH_RECOGNIZE clause
12287    fn generate_match_recognize(&mut self, mr: &MatchRecognize) -> Result<()> {
12288        use crate::dialects::DialectType;
12289
12290        // MATCH_RECOGNIZE is supported in Oracle, Snowflake, Presto, and Trino
12291        let supports_match_recognize = matches!(
12292            self.config.dialect,
12293            Some(DialectType::Oracle) | Some(DialectType::Snowflake) | Some(DialectType::Presto) | Some(DialectType::Trino)
12294        );
12295
12296        // Generate the source table first
12297        if let Some(source) = &mr.this {
12298            self.generate_expression(source)?;
12299        }
12300
12301        if !supports_match_recognize {
12302            self.write("/* MATCH_RECOGNIZE not supported in this dialect */");
12303            return Ok(());
12304        }
12305
12306        // In pretty mode, MATCH_RECOGNIZE should be on a new line
12307        if self.config.pretty {
12308            self.write_newline();
12309        } else {
12310            self.write_space();
12311        }
12312
12313        self.write_keyword("MATCH_RECOGNIZE");
12314        self.write(" (");
12315
12316        if self.config.pretty {
12317            self.indent_level += 1;
12318        }
12319
12320        let mut needs_separator = false;
12321
12322        // PARTITION BY
12323        if let Some(partition_by) = &mr.partition_by {
12324            if !partition_by.is_empty() {
12325                if self.config.pretty {
12326                    self.write_newline();
12327                    self.write_indent();
12328                }
12329                self.write_keyword("PARTITION BY");
12330                self.write_space();
12331                for (i, expr) in partition_by.iter().enumerate() {
12332                    if i > 0 {
12333                        self.write(", ");
12334                    }
12335                    self.generate_expression(expr)?;
12336                }
12337                needs_separator = true;
12338            }
12339        }
12340
12341        // ORDER BY
12342        if let Some(order_by) = &mr.order_by {
12343            if !order_by.is_empty() {
12344                if needs_separator {
12345                    if self.config.pretty {
12346                        self.write_newline();
12347                        self.write_indent();
12348                    } else {
12349                        self.write_space();
12350                    }
12351                } else if self.config.pretty {
12352                    self.write_newline();
12353                    self.write_indent();
12354                }
12355                self.write_keyword("ORDER BY");
12356                // In pretty mode, put each ORDER BY column on a new indented line
12357                if self.config.pretty {
12358                    self.indent_level += 1;
12359                    for (i, ordered) in order_by.iter().enumerate() {
12360                        if i > 0 {
12361                            self.write(",");
12362                        }
12363                        self.write_newline();
12364                        self.write_indent();
12365                        self.generate_ordered(ordered)?;
12366                    }
12367                    self.indent_level -= 1;
12368                } else {
12369                    self.write_space();
12370                    for (i, ordered) in order_by.iter().enumerate() {
12371                        if i > 0 {
12372                            self.write(", ");
12373                        }
12374                        self.generate_ordered(ordered)?;
12375                    }
12376                }
12377                needs_separator = true;
12378            }
12379        }
12380
12381        // MEASURES
12382        if let Some(measures) = &mr.measures {
12383            if !measures.is_empty() {
12384                if needs_separator {
12385                    if self.config.pretty {
12386                        self.write_newline();
12387                        self.write_indent();
12388                    } else {
12389                        self.write_space();
12390                    }
12391                } else if self.config.pretty {
12392                    self.write_newline();
12393                    self.write_indent();
12394                }
12395                self.write_keyword("MEASURES");
12396                // In pretty mode, put each MEASURE on a new indented line
12397                if self.config.pretty {
12398                    self.indent_level += 1;
12399                    for (i, measure) in measures.iter().enumerate() {
12400                        if i > 0 {
12401                            self.write(",");
12402                        }
12403                        self.write_newline();
12404                        self.write_indent();
12405                        // Handle RUNNING/FINAL prefix
12406                        if let Some(semantics) = &measure.window_frame {
12407                            match semantics {
12408                                MatchRecognizeSemantics::Running => {
12409                                    self.write_keyword("RUNNING");
12410                                    self.write_space();
12411                                }
12412                                MatchRecognizeSemantics::Final => {
12413                                    self.write_keyword("FINAL");
12414                                    self.write_space();
12415                                }
12416                            }
12417                        }
12418                        self.generate_expression(&measure.this)?;
12419                    }
12420                    self.indent_level -= 1;
12421                } else {
12422                    self.write_space();
12423                    for (i, measure) in measures.iter().enumerate() {
12424                        if i > 0 {
12425                            self.write(", ");
12426                        }
12427                        // Handle RUNNING/FINAL prefix
12428                        if let Some(semantics) = &measure.window_frame {
12429                            match semantics {
12430                                MatchRecognizeSemantics::Running => {
12431                                    self.write_keyword("RUNNING");
12432                                    self.write_space();
12433                                }
12434                                MatchRecognizeSemantics::Final => {
12435                                    self.write_keyword("FINAL");
12436                                    self.write_space();
12437                                }
12438                            }
12439                        }
12440                        self.generate_expression(&measure.this)?;
12441                    }
12442                }
12443                needs_separator = true;
12444            }
12445        }
12446
12447        // Row semantics (ONE ROW PER MATCH, ALL ROWS PER MATCH, etc.)
12448        if let Some(rows) = &mr.rows {
12449            if needs_separator {
12450                if self.config.pretty {
12451                    self.write_newline();
12452                    self.write_indent();
12453                } else {
12454                    self.write_space();
12455                }
12456            } else if self.config.pretty {
12457                self.write_newline();
12458                self.write_indent();
12459            }
12460            match rows {
12461                MatchRecognizeRows::OneRowPerMatch => {
12462                    self.write_keyword("ONE ROW PER MATCH");
12463                }
12464                MatchRecognizeRows::AllRowsPerMatch => {
12465                    self.write_keyword("ALL ROWS PER MATCH");
12466                }
12467                MatchRecognizeRows::AllRowsPerMatchShowEmptyMatches => {
12468                    self.write_keyword("ALL ROWS PER MATCH SHOW EMPTY MATCHES");
12469                }
12470                MatchRecognizeRows::AllRowsPerMatchOmitEmptyMatches => {
12471                    self.write_keyword("ALL ROWS PER MATCH OMIT EMPTY MATCHES");
12472                }
12473                MatchRecognizeRows::AllRowsPerMatchWithUnmatchedRows => {
12474                    self.write_keyword("ALL ROWS PER MATCH WITH UNMATCHED ROWS");
12475                }
12476            }
12477            needs_separator = true;
12478        }
12479
12480        // AFTER MATCH SKIP
12481        if let Some(after) = &mr.after {
12482            if needs_separator {
12483                if self.config.pretty {
12484                    self.write_newline();
12485                    self.write_indent();
12486                } else {
12487                    self.write_space();
12488                }
12489            } else if self.config.pretty {
12490                self.write_newline();
12491                self.write_indent();
12492            }
12493            match after {
12494                MatchRecognizeAfter::PastLastRow => {
12495                    self.write_keyword("AFTER MATCH SKIP PAST LAST ROW");
12496                }
12497                MatchRecognizeAfter::ToNextRow => {
12498                    self.write_keyword("AFTER MATCH SKIP TO NEXT ROW");
12499                }
12500                MatchRecognizeAfter::ToFirst(ident) => {
12501                    self.write_keyword("AFTER MATCH SKIP TO FIRST");
12502                    self.write_space();
12503                    self.generate_identifier(ident)?;
12504                }
12505                MatchRecognizeAfter::ToLast(ident) => {
12506                    self.write_keyword("AFTER MATCH SKIP TO LAST");
12507                    self.write_space();
12508                    self.generate_identifier(ident)?;
12509                }
12510            }
12511            needs_separator = true;
12512        }
12513
12514        // PATTERN
12515        if let Some(pattern) = &mr.pattern {
12516            if needs_separator {
12517                if self.config.pretty {
12518                    self.write_newline();
12519                    self.write_indent();
12520                } else {
12521                    self.write_space();
12522                }
12523            } else if self.config.pretty {
12524                self.write_newline();
12525                self.write_indent();
12526            }
12527            self.write_keyword("PATTERN");
12528            self.write_space();
12529            self.write("(");
12530            self.write(pattern);
12531            self.write(")");
12532            needs_separator = true;
12533        }
12534
12535        // DEFINE
12536        if let Some(define) = &mr.define {
12537            if !define.is_empty() {
12538                if needs_separator {
12539                    if self.config.pretty {
12540                        self.write_newline();
12541                        self.write_indent();
12542                    } else {
12543                        self.write_space();
12544                    }
12545                } else if self.config.pretty {
12546                    self.write_newline();
12547                    self.write_indent();
12548                }
12549                self.write_keyword("DEFINE");
12550                // In pretty mode, put each DEFINE on a new indented line
12551                if self.config.pretty {
12552                    self.indent_level += 1;
12553                    for (i, (name, expr)) in define.iter().enumerate() {
12554                        if i > 0 {
12555                            self.write(",");
12556                        }
12557                        self.write_newline();
12558                        self.write_indent();
12559                        self.generate_identifier(name)?;
12560                        self.write(" AS ");
12561                        self.generate_expression(expr)?;
12562                    }
12563                    self.indent_level -= 1;
12564                } else {
12565                    self.write_space();
12566                    for (i, (name, expr)) in define.iter().enumerate() {
12567                        if i > 0 {
12568                            self.write(", ");
12569                        }
12570                        self.generate_identifier(name)?;
12571                        self.write(" AS ");
12572                        self.generate_expression(expr)?;
12573                    }
12574                }
12575            }
12576        }
12577
12578        if self.config.pretty {
12579            self.indent_level -= 1;
12580            self.write_newline();
12581        }
12582        self.write(")");
12583
12584        // Alias - only include AS if it was explicitly present in the input
12585        if let Some(alias) = &mr.alias {
12586            self.write(" ");
12587            if mr.alias_explicit_as {
12588                self.write_keyword("AS");
12589                self.write(" ");
12590            }
12591            self.generate_identifier(alias)?;
12592        }
12593
12594        Ok(())
12595    }
12596
12597    /// Generate a query hint /*+ ... */
12598    fn generate_hint(&mut self, hint: &Hint) -> Result<()> {
12599        use crate::dialects::DialectType;
12600
12601        // Output hints for dialects that support them, or when no dialect is specified (identity tests)
12602        let supports_hints = matches!(
12603            self.config.dialect,
12604            None |  // No dialect = preserve everything
12605            Some(DialectType::Oracle) | Some(DialectType::MySQL) |
12606            Some(DialectType::Spark) | Some(DialectType::Hive) |
12607            Some(DialectType::Databricks) | Some(DialectType::PostgreSQL)
12608        );
12609
12610        if !supports_hints || hint.expressions.is_empty() {
12611            return Ok(());
12612        }
12613
12614        // First, expand raw hint text into individual hint strings
12615        // This handles the case where the parser stored multiple hints as a single raw string
12616        let mut hint_strings: Vec<String> = Vec::new();
12617        for expr in &hint.expressions {
12618            match expr {
12619                HintExpression::Raw(text) => {
12620                    // Parse raw hint text into individual hint function calls
12621                    let parsed = self.parse_raw_hint_text(text);
12622                    hint_strings.extend(parsed);
12623                }
12624                _ => {
12625                    hint_strings.push(self.hint_expression_to_string(expr)?);
12626                }
12627            }
12628        }
12629
12630        // In pretty mode with multiple hints, always use multiline format
12631        // This matches Python sqlglot's behavior where expressions() with default dynamic=False
12632        // always joins with newlines in pretty mode
12633        let use_multiline = self.config.pretty && hint_strings.len() > 1;
12634
12635        if use_multiline {
12636            // Pretty print with each hint on its own line
12637            self.write(" /*+ ");
12638            for (i, hint_str) in hint_strings.iter().enumerate() {
12639                if i > 0 {
12640                    self.write_newline();
12641                    self.write("  "); // 2-space indent within hint block
12642                }
12643                self.write(hint_str);
12644            }
12645            self.write(" */");
12646        } else {
12647            // Single line format
12648            self.write(" /*+ ");
12649            let sep = match self.config.dialect {
12650                Some(DialectType::Spark) | Some(DialectType::Databricks) => ", ",
12651                _ => " ",
12652            };
12653            for (i, hint_str) in hint_strings.iter().enumerate() {
12654                if i > 0 {
12655                    self.write(sep);
12656                }
12657                self.write(hint_str);
12658            }
12659            self.write(" */");
12660        }
12661
12662        Ok(())
12663    }
12664
12665    /// Parse raw hint text into individual hint function calls
12666    /// e.g., "LEADING(a b) USE_NL(c)" -> ["LEADING(a b)", "USE_NL(c)"]
12667    /// If the hint contains unparseable content (like SQL keywords), return as single raw string
12668    fn parse_raw_hint_text(&self, text: &str) -> Vec<String> {
12669        let mut results = Vec::new();
12670        let mut chars = text.chars().peekable();
12671        let mut current = String::new();
12672        let mut paren_depth = 0;
12673        let mut has_unparseable_content = false;
12674        let mut position_after_last_function = 0;
12675        let mut char_position = 0;
12676
12677        while let Some(c) = chars.next() {
12678            char_position += c.len_utf8();
12679            match c {
12680                '(' => {
12681                    paren_depth += 1;
12682                    current.push(c);
12683                }
12684                ')' => {
12685                    paren_depth -= 1;
12686                    current.push(c);
12687                    // When we close the outer parenthesis, we've completed a hint function
12688                    if paren_depth == 0 {
12689                        let trimmed = current.trim().to_string();
12690                        if !trimmed.is_empty() {
12691                            // Format this hint for pretty printing if needed
12692                            let formatted = self.format_hint_function(&trimmed);
12693                            results.push(formatted);
12694                        }
12695                        current.clear();
12696                        position_after_last_function = char_position;
12697                    }
12698                }
12699                ' ' | '\t' | '\n' | ',' if paren_depth == 0 => {
12700                    // Space/comma/whitespace outside parentheses - skip
12701                }
12702                _ if paren_depth == 0 => {
12703                    // Character outside parentheses - accumulate for potential hint name
12704                    current.push(c);
12705                }
12706                _ => {
12707                    current.push(c);
12708                }
12709            }
12710        }
12711
12712        // Check if there's remaining text after the last function call
12713        let remaining_text = text[position_after_last_function..].trim();
12714        if !remaining_text.is_empty() {
12715            // Check if it looks like valid hint function names
12716            // Valid hint identifiers typically are uppercase alphanumeric with underscores
12717            // If we see multiple words without parens, it's likely unparseable
12718            let words: Vec<&str> = remaining_text.split_whitespace().collect();
12719            let looks_like_hint_functions = words.iter().all(|word| {
12720                // A valid hint name followed by opening paren, or a standalone uppercase identifier
12721                word.contains('(') || (word.chars().all(|c| c.is_ascii_uppercase() || c == '_'))
12722            });
12723
12724            if !looks_like_hint_functions && words.len() > 1 {
12725                has_unparseable_content = true;
12726            }
12727        }
12728
12729        // If we detected unparseable content (like SQL keywords), return the whole hint as-is
12730        if has_unparseable_content {
12731            return vec![text.trim().to_string()];
12732        }
12733
12734        // If we couldn't parse anything, return the original text as a single hint
12735        if results.is_empty() {
12736            results.push(text.trim().to_string());
12737        }
12738
12739        results
12740    }
12741
12742    /// Format a hint function for pretty printing
12743    /// e.g., "LEADING(aaa bbb ccc ddd)" -> multiline if args are too wide
12744    fn format_hint_function(&self, hint: &str) -> String {
12745        if !self.config.pretty {
12746            return hint.to_string();
12747        }
12748
12749        // Try to parse NAME(args) pattern
12750        if let Some(paren_pos) = hint.find('(') {
12751            if hint.ends_with(')') {
12752                let name = &hint[..paren_pos];
12753                let args_str = &hint[paren_pos + 1..hint.len() - 1];
12754
12755                // Parse arguments (space-separated for Oracle hints)
12756                let args: Vec<&str> = args_str.split_whitespace().collect();
12757
12758                // Calculate total width of arguments
12759                let total_args_width: usize = args.iter().map(|s| s.len()).sum::<usize>()
12760                    + args.len().saturating_sub(1); // spaces between args
12761
12762                // If too wide, format on multiple lines
12763                if total_args_width > self.config.max_text_width && !args.is_empty() {
12764                    let mut result = format!("{}(\n", name);
12765                    for arg in &args {
12766                        result.push_str("    "); // 4-space indent for args
12767                        result.push_str(arg);
12768                        result.push('\n');
12769                    }
12770                    result.push_str("  )"); // 2-space indent for closing paren
12771                    return result;
12772                }
12773            }
12774        }
12775
12776        hint.to_string()
12777    }
12778
12779    /// Convert a hint expression to a string, handling multiline formatting for long arguments
12780    fn hint_expression_to_string(&mut self, expr: &HintExpression) -> Result<String> {
12781        match expr {
12782            HintExpression::Function { name, args } => {
12783                // Generate each argument to a string
12784                let arg_strings: Vec<String> = args.iter()
12785                    .map(|arg| {
12786                        let mut gen = Generator::with_config(self.config.clone());
12787                        gen.generate_expression(arg)?;
12788                        Ok(gen.output)
12789                    })
12790                    .collect::<Result<Vec<_>>>()?;
12791
12792                // Oracle hints use space-separated arguments, not comma-separated
12793                let total_args_width: usize = arg_strings.iter().map(|s| s.len()).sum::<usize>()
12794                    + arg_strings.len().saturating_sub(1); // spaces between args
12795
12796                // Check if function args need multiline formatting
12797                // Use too_wide check for argument formatting
12798                let args_multiline = self.config.pretty
12799                    && total_args_width > self.config.max_text_width;
12800
12801                if args_multiline && !arg_strings.is_empty() {
12802                    // Multiline format for long argument lists
12803                    let mut result = format!("{}(\n", name);
12804                    for arg_str in &arg_strings {
12805                        result.push_str("    "); // 4-space indent for args
12806                        result.push_str(arg_str);
12807                        result.push('\n');
12808                    }
12809                    result.push_str("  )"); // 2-space indent for closing paren
12810                    Ok(result)
12811                } else {
12812                    // Single line format with space-separated args (Oracle style)
12813                    let args_str = arg_strings.join(" ");
12814                    Ok(format!("{}({})", name, args_str))
12815                }
12816            }
12817            HintExpression::Identifier(name) => Ok(name.clone()),
12818            HintExpression::Raw(text) => {
12819                // For pretty printing, try to format the raw text
12820                if self.config.pretty {
12821                    Ok(self.format_hint_function(text))
12822                } else {
12823                    Ok(text.clone())
12824                }
12825            }
12826        }
12827    }
12828
12829    fn generate_table(&mut self, table: &TableRef) -> Result<()> {
12830        // PostgreSQL ONLY modifier: prevents scanning child tables
12831        if table.only {
12832            self.write_keyword("ONLY");
12833            self.write_space();
12834        }
12835
12836        // Check for Snowflake IDENTIFIER() function
12837        if let Some(ref identifier_func) = table.identifier_func {
12838            self.generate_expression(identifier_func)?;
12839        } else {
12840            if let Some(catalog) = &table.catalog {
12841                self.generate_identifier(catalog)?;
12842                self.write(".");
12843            }
12844            if let Some(schema) = &table.schema {
12845                self.generate_identifier(schema)?;
12846                self.write(".");
12847            }
12848            self.generate_identifier(&table.name)?;
12849        }
12850
12851        // Output Snowflake CHANGES clause (before partition, includes its own AT/BEFORE/END)
12852        if let Some(changes) = &table.changes {
12853            self.write(" ");
12854            self.generate_changes(changes)?;
12855        }
12856
12857        // Output MySQL PARTITION clause: t1 PARTITION(p0, p1)
12858        if !table.partitions.is_empty() {
12859            self.write_space();
12860            self.write_keyword("PARTITION");
12861            self.write("(");
12862            for (i, partition) in table.partitions.iter().enumerate() {
12863                if i > 0 {
12864                    self.write(", ");
12865                }
12866                self.generate_identifier(partition)?;
12867            }
12868            self.write(")");
12869        }
12870
12871        // Output time travel clause: BEFORE (STATEMENT => ...) or AT (TIMESTAMP => ...)
12872        // Skip if CHANGES clause is present (CHANGES includes its own time travel)
12873        if table.changes.is_none() {
12874            if let Some(when) = &table.when {
12875                self.write_space();
12876                self.generate_historical_data(when)?;
12877            }
12878        }
12879
12880        // Output TSQL FOR SYSTEM_TIME temporal clause
12881        if let Some(ref system_time) = table.system_time {
12882            self.write_space();
12883            self.write(system_time);
12884        }
12885
12886        // Output Presto/Trino time travel: FOR VERSION AS OF / FOR TIMESTAMP AS OF
12887        if let Some(ref version) = table.version {
12888            self.write_space();
12889            self.generate_version(version)?;
12890        }
12891
12892        // When alias_post_tablesample is true, the order is: table TABLESAMPLE (...) alias
12893        // When alias_post_tablesample is false (default), the order is: table alias TABLESAMPLE (...)
12894        // Oracle, Hive, Spark use ALIAS_POST_TABLESAMPLE = true (alias comes after sample)
12895        let alias_post_tablesample = self.config.alias_post_tablesample;
12896
12897        if alias_post_tablesample {
12898            // TABLESAMPLE before alias (Oracle, Hive, Spark)
12899            self.generate_table_sample_clause(table)?;
12900        }
12901
12902        // Output table hints (TSQL: WITH (TABLOCK, INDEX(myindex), ...))
12903        // For SQLite, INDEXED BY hints come after the alias, so skip here
12904        let is_sqlite_hint = matches!(self.config.dialect, Some(DialectType::SQLite))
12905            && table.hints.iter().any(|h| {
12906                if let Expression::Identifier(id) = h {
12907                    id.name.starts_with("INDEXED BY") || id.name == "NOT INDEXED"
12908                } else {
12909                    false
12910                }
12911            });
12912        if !table.hints.is_empty() && !is_sqlite_hint {
12913            for hint in &table.hints {
12914                self.write_space();
12915                self.generate_expression(hint)?;
12916            }
12917        }
12918
12919        if let Some(alias) = &table.alias {
12920            self.write_space();
12921            // Output AS if it was explicitly present in the input, OR for certain dialects/cases
12922            // PostgreSQL/Redshift/Snowflake/BigQuery/Presto/Trino always use AS for table aliases
12923            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));
12924            let is_stage_ref = table.name.name.starts_with('@');
12925            if table.alias_explicit_as || always_use_as || is_stage_ref {
12926                self.write_keyword("AS");
12927                self.write_space();
12928            }
12929            self.generate_identifier(alias)?;
12930
12931            // Output column aliases if present: AS t(c1, c2)
12932            // Skip for dialects that don't support table alias columns (BigQuery, SQLite)
12933            if !table.column_aliases.is_empty() && self.config.supports_table_alias_columns {
12934                self.write("(");
12935                for (i, col_alias) in table.column_aliases.iter().enumerate() {
12936                    if i > 0 {
12937                        self.write(", ");
12938                    }
12939                    self.generate_identifier(col_alias)?;
12940                }
12941                self.write(")");
12942            }
12943        }
12944
12945        // For default behavior (alias_post_tablesample = false), output TABLESAMPLE after alias
12946        if !alias_post_tablesample {
12947            self.generate_table_sample_clause(table)?;
12948        }
12949
12950        // Output SQLite INDEXED BY / NOT INDEXED hints after alias
12951        if is_sqlite_hint {
12952            for hint in &table.hints {
12953                self.write_space();
12954                self.generate_expression(hint)?;
12955            }
12956        }
12957
12958        // ClickHouse FINAL modifier
12959        if table.final_ && matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
12960            self.write_space();
12961            self.write_keyword("FINAL");
12962        }
12963
12964        // Output trailing comments
12965        for comment in &table.trailing_comments {
12966            self.write_space();
12967            self.write_formatted_comment(comment);
12968        }
12969
12970        Ok(())
12971    }
12972
12973    /// Helper to output TABLESAMPLE clause for a table reference
12974    fn generate_table_sample_clause(&mut self, table: &TableRef) -> Result<()> {
12975        if let Some(ref ts) = table.table_sample {
12976            self.write_space();
12977            if ts.is_using_sample {
12978                self.write_keyword("USING SAMPLE");
12979            } else {
12980                // Use the configured tablesample keyword (e.g., "TABLESAMPLE" or "SAMPLE")
12981                self.write_keyword(self.config.tablesample_keywords);
12982            }
12983            self.generate_sample_body(ts)?;
12984            // Seed for table-level sample - use dialect's configured keyword
12985            if let Some(ref seed) = ts.seed {
12986                self.write_space();
12987                self.write_keyword(self.config.tablesample_seed_keyword);
12988                self.write(" (");
12989                self.generate_expression(seed)?;
12990                self.write(")");
12991            }
12992        }
12993        Ok(())
12994    }
12995
12996    fn generate_stage_reference(&mut self, sr: &StageReference) -> Result<()> {
12997        // Output: '@stage_name/path' if quoted, or @stage_name/path otherwise
12998        // Optionally followed by (FILE_FORMAT => 'fmt', PATTERN => '*.csv')
12999
13000        if sr.quoted {
13001            self.write("'");
13002        }
13003
13004        self.write(&sr.name);
13005        if let Some(path) = &sr.path {
13006            self.write(path);
13007        }
13008
13009        if sr.quoted {
13010            self.write("'");
13011        }
13012
13013        // Output FILE_FORMAT and PATTERN if present
13014        let has_options = sr.file_format.is_some() || sr.pattern.is_some();
13015        if has_options {
13016            self.write(" (");
13017            let mut first = true;
13018
13019            if let Some(file_format) = &sr.file_format {
13020                if !first {
13021                    self.write(", ");
13022                }
13023                self.write_keyword("FILE_FORMAT");
13024                self.write(" => ");
13025                self.generate_expression(file_format)?;
13026                first = false;
13027            }
13028
13029            if let Some(pattern) = &sr.pattern {
13030                if !first {
13031                    self.write(", ");
13032                }
13033                self.write_keyword("PATTERN");
13034                self.write(" => '");
13035                self.write(pattern);
13036                self.write("'");
13037            }
13038
13039            self.write(")");
13040        }
13041        Ok(())
13042    }
13043
13044
13045    fn generate_star(&mut self, star: &Star) -> Result<()> {
13046        use crate::dialects::DialectType;
13047
13048        if let Some(table) = &star.table {
13049            self.generate_identifier(table)?;
13050            self.write(".");
13051        }
13052        self.write("*");
13053
13054        // Generate EXCLUDE/EXCEPT clause based on dialect
13055        if let Some(except) = &star.except {
13056            if !except.is_empty() {
13057                self.write_space();
13058                // Use dialect-appropriate keyword
13059                match self.config.dialect {
13060                    Some(DialectType::BigQuery) => self.write_keyword("EXCEPT"),
13061                    Some(DialectType::DuckDB) | Some(DialectType::Snowflake) => {
13062                        self.write_keyword("EXCLUDE")
13063                    }
13064                    _ => self.write_keyword("EXCEPT"), // Default to EXCEPT
13065                }
13066                self.write(" (");
13067                for (i, col) in except.iter().enumerate() {
13068                    if i > 0 {
13069                        self.write(", ");
13070                    }
13071                    self.generate_identifier(col)?;
13072                }
13073                self.write(")");
13074            }
13075        }
13076
13077        // Generate REPLACE clause
13078        if let Some(replace) = &star.replace {
13079            if !replace.is_empty() {
13080                self.write_space();
13081                self.write_keyword("REPLACE");
13082                self.write(" (");
13083                for (i, alias) in replace.iter().enumerate() {
13084                    if i > 0 {
13085                        self.write(", ");
13086                    }
13087                    self.generate_expression(&alias.this)?;
13088                    self.write_space();
13089                    self.write_keyword("AS");
13090                    self.write_space();
13091                    self.generate_identifier(&alias.alias)?;
13092                }
13093                self.write(")");
13094            }
13095        }
13096
13097        // Generate RENAME clause (Snowflake specific)
13098        if let Some(rename) = &star.rename {
13099            if !rename.is_empty() {
13100                self.write_space();
13101                self.write_keyword("RENAME");
13102                self.write(" (");
13103                for (i, (old_name, new_name)) in rename.iter().enumerate() {
13104                    if i > 0 {
13105                        self.write(", ");
13106                    }
13107                    self.generate_identifier(old_name)?;
13108                    self.write_space();
13109                    self.write_keyword("AS");
13110                    self.write_space();
13111                    self.generate_identifier(new_name)?;
13112                }
13113                self.write(")");
13114            }
13115        }
13116
13117        // Output trailing comments
13118        for comment in &star.trailing_comments {
13119            self.write_space();
13120            self.write(comment);
13121        }
13122
13123        Ok(())
13124    }
13125
13126    /// Generate Snowflake braced wildcard syntax: {*}, {tbl.*}, {* EXCLUDE (...)}, {* ILIKE '...'}
13127    fn generate_braced_wildcard(&mut self, expr: &Expression) -> Result<()> {
13128        self.write("{");
13129        match expr {
13130            Expression::Star(star) => {
13131                // Generate the star (table.* or just * with optional EXCLUDE)
13132                self.generate_star(star)?;
13133            }
13134            Expression::ILike(ilike) => {
13135                // {* ILIKE 'pattern'} syntax
13136                self.generate_expression(&ilike.left)?;
13137                self.write_space();
13138                self.write_keyword("ILIKE");
13139                self.write_space();
13140                self.generate_expression(&ilike.right)?;
13141            }
13142            _ => {
13143                self.generate_expression(expr)?;
13144            }
13145        }
13146        self.write("}");
13147        Ok(())
13148    }
13149
13150    fn generate_alias(&mut self, alias: &Alias) -> Result<()> {
13151        // Generate inner expression, but skip trailing comments if they're in pre_alias_comments
13152        // to avoid duplication (comments are captured as both Column.trailing_comments
13153        // and Alias.pre_alias_comments during parsing)
13154        match &alias.this {
13155            Expression::Column(col) => {
13156                // Generate column without trailing comments - they're in pre_alias_comments
13157                if let Some(table) = &col.table {
13158                    self.generate_identifier(table)?;
13159                    self.write(".");
13160                }
13161                self.generate_identifier(&col.name)?;
13162            }
13163            _ => {
13164                self.generate_expression(&alias.this)?;
13165            }
13166        }
13167
13168        // Output pre-alias comments (comments between expression and AS)
13169        for comment in &alias.pre_alias_comments {
13170            self.write_space();
13171            self.write(comment);
13172        }
13173
13174        use crate::dialects::DialectType;
13175
13176        // Determine if we should skip AS keyword for table-valued function aliases
13177        // Oracle and some other dialects don't use AS for table aliases
13178        // Note: We specifically use TableFromRows here, NOT Function, because Function
13179        // matches regular functions like MATCH_NUMBER() which should include the AS keyword.
13180        // TableFromRows represents TABLE(expr) constructs which are actual table-valued functions.
13181        let is_table_source = matches!(
13182            &alias.this,
13183            Expression::JSONTable(_)
13184                | Expression::XMLTable(_)
13185                | Expression::TableFromRows(_)
13186                | Expression::Unnest(_)
13187                | Expression::MatchRecognize(_)
13188                | Expression::Select(_)
13189                | Expression::Subquery(_)
13190                | Expression::Paren(_)
13191        );
13192        let dialect_skips_table_alias_as = matches!(
13193            self.config.dialect,
13194            Some(DialectType::Oracle)
13195        );
13196        let skip_as = is_table_source && dialect_skips_table_alias_as;
13197
13198        self.write_space();
13199        if !skip_as {
13200            self.write_keyword("AS");
13201            self.write_space();
13202        }
13203
13204        // BigQuery doesn't support column aliases in table aliases: AS t(c1, c2)
13205        let skip_column_aliases = matches!(self.config.dialect, Some(DialectType::BigQuery));
13206
13207        // Check if we have column aliases only (no table alias name)
13208        if alias.alias.is_empty() && !alias.column_aliases.is_empty() && !skip_column_aliases {
13209            // Generate AS (col1, col2, ...)
13210            self.write("(");
13211            for (i, col_alias) in alias.column_aliases.iter().enumerate() {
13212                if i > 0 {
13213                    self.write(", ");
13214                }
13215                self.generate_alias_identifier(col_alias)?;
13216            }
13217            self.write(")");
13218        } else if !alias.column_aliases.is_empty() && !skip_column_aliases {
13219            // Generate AS alias(col1, col2, ...)
13220            self.generate_alias_identifier(&alias.alias)?;
13221            self.write("(");
13222            for (i, col_alias) in alias.column_aliases.iter().enumerate() {
13223                if i > 0 {
13224                    self.write(", ");
13225                }
13226                self.generate_alias_identifier(col_alias)?;
13227            }
13228            self.write(")");
13229        } else {
13230            // Simple alias (or BigQuery without column aliases)
13231            self.generate_alias_identifier(&alias.alias)?;
13232        }
13233
13234        // Output trailing comments (comments after the alias)
13235        for comment in &alias.trailing_comments {
13236            self.write_space();
13237            self.write(comment);
13238        }
13239
13240        Ok(())
13241    }
13242
13243    fn generate_cast(&mut self, cast: &Cast) -> Result<()> {
13244        use crate::dialects::DialectType;
13245
13246        // SingleStore uses :> syntax
13247        if matches!(self.config.dialect, Some(DialectType::SingleStore)) {
13248            self.generate_expression(&cast.this)?;
13249            self.write(" :> ");
13250            self.generate_data_type(&cast.to)?;
13251            return Ok(());
13252        }
13253
13254        // Teradata: CAST(x AS FORMAT 'fmt') (no data type)
13255        if matches!(self.config.dialect, Some(DialectType::Teradata)) {
13256            let is_unknown_type = matches!(cast.to, DataType::Unknown)
13257                || matches!(cast.to, DataType::Custom { ref name } if name.is_empty());
13258            if is_unknown_type {
13259                if let Some(format) = &cast.format {
13260                    self.write_keyword("CAST");
13261                    self.write("(");
13262                    self.generate_expression(&cast.this)?;
13263                    self.write_space();
13264                    self.write_keyword("AS");
13265                    self.write_space();
13266                    self.write_keyword("FORMAT");
13267                    self.write_space();
13268                    self.generate_expression(format)?;
13269                    self.write(")");
13270                    return Ok(());
13271                }
13272            }
13273        }
13274
13275        // Oracle: CAST(x AS DATE/TIMESTAMP ..., 'format') -> TO_DATE/TO_TIMESTAMP(x, 'format')
13276        // This follows Python sqlglot's behavior of transforming CAST with format to native functions
13277        if matches!(self.config.dialect, Some(DialectType::Oracle)) {
13278            if let Some(format) = &cast.format {
13279                // Check if target type is DATE or TIMESTAMP
13280                let is_date = matches!(cast.to, DataType::Date);
13281                let is_timestamp = matches!(cast.to, DataType::Timestamp { .. });
13282
13283                if is_date || is_timestamp {
13284                    let func_name = if is_date { "TO_DATE" } else { "TO_TIMESTAMP" };
13285                    self.write_keyword(func_name);
13286                    self.write("(");
13287                    self.generate_expression(&cast.this)?;
13288                    self.write(", ");
13289
13290                    // Normalize format string for Oracle (HH -> HH12)
13291                    // Oracle HH is 12-hour format, same as HH12. For clarity, Python sqlglot uses HH12.
13292                    if let Expression::Literal(Literal::String(fmt_str)) = format.as_ref() {
13293                        let normalized = self.normalize_oracle_format(fmt_str);
13294                        self.write("'");
13295                        self.write(&normalized);
13296                        self.write("'");
13297                    } else {
13298                        self.generate_expression(format)?;
13299                    }
13300
13301                    self.write(")");
13302                    return Ok(());
13303                }
13304            }
13305        }
13306
13307        // BigQuery: CAST(ARRAY[...] AS ARRAY<T>) -> ARRAY<T>[...]
13308        // This preserves sqlglot's typed inline array literal output.
13309        if matches!(self.config.dialect, Some(DialectType::BigQuery)) {
13310            if let Expression::Array(arr) = &cast.this {
13311                self.generate_data_type(&cast.to)?;
13312                // Output just the bracket content [values] without the ARRAY prefix
13313                self.write("[");
13314                for (i, expr) in arr.expressions.iter().enumerate() {
13315                    if i > 0 {
13316                        self.write(", ");
13317                    }
13318                    self.generate_expression(expr)?;
13319                }
13320                self.write("]");
13321                return Ok(());
13322            }
13323            if matches!(&cast.this, Expression::ArrayFunc(_)) {
13324                self.generate_data_type(&cast.to)?;
13325                self.generate_expression(&cast.this)?;
13326                return Ok(());
13327            }
13328        }
13329
13330        // DuckDB/Presto/Trino: When CAST(Struct([unnamed]) AS STRUCT(...)),
13331        // convert the inner Struct to ROW(values...) format
13332        if matches!(self.config.dialect, Some(DialectType::DuckDB) | Some(DialectType::Presto) | Some(DialectType::Trino)) {
13333            if let Expression::Struct(ref s) = cast.this {
13334                let all_unnamed = s.fields.iter().all(|(name, _)| name.is_none());
13335                if all_unnamed && matches!(cast.to, DataType::Struct { .. }) {
13336                    self.write_keyword("CAST");
13337                    self.write("(");
13338                    self.generate_struct_as_row(s)?;
13339                    self.write_space();
13340                    self.write_keyword("AS");
13341                    self.write_space();
13342                    self.generate_data_type(&cast.to)?;
13343                    self.write(")");
13344                    return Ok(());
13345                }
13346            }
13347        }
13348
13349        // Determine if we should use :: syntax based on dialect
13350        // PostgreSQL prefers :: for identity, most others prefer CAST()
13351        let use_double_colon = cast.double_colon_syntax && self.dialect_prefers_double_colon();
13352
13353        if use_double_colon {
13354            // PostgreSQL :: syntax: expr::type
13355            self.generate_expression(&cast.this)?;
13356            self.write("::");
13357            self.generate_data_type(&cast.to)?;
13358        } else {
13359            // Standard CAST() syntax
13360            self.write_keyword("CAST");
13361            self.write("(");
13362            self.generate_expression(&cast.this)?;
13363            self.write_space();
13364            self.write_keyword("AS");
13365            self.write_space();
13366            // For MySQL/SingleStore/TiDB, map text/blob variant Custom types to CHAR in CAST
13367            // This matches Python sqlglot's CHAR_CAST_MAPPING behavior
13368            if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB)) {
13369                if let DataType::Custom { ref name } = cast.to {
13370                    let upper = name.to_uppercase();
13371                    match upper.as_str() {
13372                        "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" | "LONGBLOB" | "MEDIUMBLOB" | "TINYBLOB" => {
13373                            self.write_keyword("CHAR");
13374                        }
13375                        _ => {
13376                            self.generate_data_type(&cast.to)?;
13377                        }
13378                    }
13379                } else {
13380                    self.generate_data_type(&cast.to)?;
13381                }
13382            } else {
13383                self.generate_data_type(&cast.to)?;
13384            }
13385
13386            // Output DEFAULT ... ON CONVERSION ERROR clause if present (Oracle)
13387            if let Some(default) = &cast.default {
13388                self.write_space();
13389                self.write_keyword("DEFAULT");
13390                self.write_space();
13391                self.generate_expression(default)?;
13392                self.write_space();
13393                self.write_keyword("ON");
13394                self.write_space();
13395                self.write_keyword("CONVERSION");
13396                self.write_space();
13397                self.write_keyword("ERROR");
13398            }
13399
13400            // Output FORMAT clause if present (BigQuery: CAST(x AS STRING FORMAT 'format'))
13401            // For Oracle with comma-separated format: CAST(x AS DATE DEFAULT NULL ON CONVERSION ERROR, 'format')
13402            if let Some(format) = &cast.format {
13403                // Check if Oracle dialect - use comma syntax
13404                if matches!(self.config.dialect, Some(crate::dialects::DialectType::Oracle)) {
13405                    self.write(", ");
13406                } else {
13407                    self.write_space();
13408                    self.write_keyword("FORMAT");
13409                    self.write_space();
13410                }
13411                self.generate_expression(format)?;
13412            }
13413
13414            self.write(")");
13415            // Output trailing comments
13416            for comment in &cast.trailing_comments {
13417                self.write_space();
13418                self.write(comment);
13419            }
13420        }
13421        Ok(())
13422    }
13423
13424    /// Generate a Struct as ROW(values...) format, recursively converting inner Struct to ROW too.
13425    /// Used for DuckDB/Presto/Trino CAST(Struct AS STRUCT(...)) context.
13426    fn generate_struct_as_row(&mut self, s: &crate::expressions::Struct) -> Result<()> {
13427        self.write_keyword("ROW");
13428        self.write("(");
13429        for (i, (_, expr)) in s.fields.iter().enumerate() {
13430            if i > 0 { self.write(", "); }
13431            // Recursively convert inner Struct to ROW format
13432            if let Expression::Struct(ref inner_s) = expr {
13433                self.generate_struct_as_row(inner_s)?;
13434            } else {
13435                self.generate_expression(expr)?;
13436            }
13437        }
13438        self.write(")");
13439        Ok(())
13440    }
13441
13442    /// Normalize Oracle date/time format strings
13443    /// HH -> HH12 (both are 12-hour format, but Python sqlglot prefers explicit HH12)
13444    fn normalize_oracle_format(&self, format: &str) -> String {
13445        // Replace standalone HH with HH12 (but not HH12 or HH24)
13446        // We need to be careful not to replace HH12 -> HH1212 or HH24 -> HH1224
13447        let mut result = String::new();
13448        let chars: Vec<char> = format.chars().collect();
13449        let mut i = 0;
13450
13451        while i < chars.len() {
13452            if i + 1 < chars.len() && chars[i] == 'H' && chars[i + 1] == 'H' {
13453                // Check what follows HH
13454                if i + 2 < chars.len() {
13455                    let next = chars[i + 2];
13456                    if next == '1' || next == '2' {
13457                        // This is HH12 or HH24, keep as is
13458                        result.push('H');
13459                        result.push('H');
13460                        i += 2;
13461                        continue;
13462                    }
13463                }
13464                // Standalone HH -> HH12
13465                result.push_str("HH12");
13466                i += 2;
13467            } else {
13468                result.push(chars[i]);
13469                i += 1;
13470            }
13471        }
13472
13473        result
13474    }
13475
13476    /// Check if the current dialect prefers :: cast syntax
13477    /// Note: Python sqlglot normalizes all :: to CAST() for output, even for PostgreSQL
13478    /// So we return false for all dialects to match Python sqlglot's behavior
13479    fn dialect_prefers_double_colon(&self) -> bool {
13480        // Python sqlglot normalizes :: syntax to CAST() for all dialects
13481        // Even PostgreSQL outputs CAST() not ::
13482        false
13483    }
13484
13485    /// Generate MOD function - uses % operator for Snowflake/MySQL/Presto/Trino, MOD() for others
13486    fn generate_mod_func(&mut self, f: &crate::expressions::BinaryFunc) -> Result<()> {
13487        use crate::dialects::DialectType;
13488
13489        // Snowflake, MySQL, Presto, Trino, PostgreSQL, and DuckDB prefer x % y instead of MOD(x, y)
13490        let use_percent_operator = matches!(
13491            self.config.dialect,
13492            Some(DialectType::Snowflake) | Some(DialectType::MySQL) | Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::PostgreSQL) | Some(DialectType::DuckDB)
13493        );
13494
13495        if use_percent_operator {
13496            self.generate_expression(&f.this)?;
13497            self.write(" % ");
13498            self.generate_expression(&f.expression)?;
13499            Ok(())
13500        } else {
13501            self.generate_binary_func("MOD", &f.this, &f.expression)
13502        }
13503    }
13504
13505    /// Generate IFNULL - uses COALESCE for Snowflake, IFNULL for others
13506    fn generate_ifnull(&mut self, f: &crate::expressions::BinaryFunc) -> Result<()> {
13507        use crate::dialects::DialectType;
13508
13509        // Snowflake normalizes IFNULL to COALESCE
13510        let func_name = match self.config.dialect {
13511            Some(DialectType::Snowflake) => "COALESCE",
13512            _ => "IFNULL",
13513        };
13514
13515        self.generate_binary_func(func_name, &f.this, &f.expression)
13516    }
13517
13518    /// Generate NVL - preserves original name if available, otherwise uses dialect-specific output
13519    fn generate_nvl(&mut self, f: &crate::expressions::BinaryFunc) -> Result<()> {
13520        // Use original function name if preserved (for identity tests)
13521        if let Some(ref original_name) = f.original_name {
13522            return self.generate_binary_func(original_name, &f.this, &f.expression);
13523        }
13524
13525        // Otherwise, use dialect-specific function names
13526        use crate::dialects::DialectType;
13527        let func_name = match self.config.dialect {
13528            Some(DialectType::Snowflake) |
13529            Some(DialectType::ClickHouse) |
13530            Some(DialectType::PostgreSQL) |
13531            Some(DialectType::Presto) |
13532            Some(DialectType::Trino) |
13533            Some(DialectType::Athena) |
13534            Some(DialectType::DuckDB) |
13535            Some(DialectType::BigQuery) |
13536            Some(DialectType::Spark) |
13537            Some(DialectType::Databricks) |
13538            Some(DialectType::Hive) => "COALESCE",
13539            Some(DialectType::MySQL) |
13540            Some(DialectType::Doris) |
13541            Some(DialectType::StarRocks) |
13542            Some(DialectType::SingleStore) |
13543            Some(DialectType::TiDB) => "IFNULL",
13544            _ => "NVL",
13545        };
13546
13547        self.generate_binary_func(func_name, &f.this, &f.expression)
13548    }
13549
13550    /// Generate STDDEV_SAMP - uses STDDEV for Snowflake, STDDEV_SAMP for others
13551    fn generate_stddev_samp(&mut self, f: &crate::expressions::AggFunc) -> Result<()> {
13552        use crate::dialects::DialectType;
13553
13554        // Snowflake normalizes STDDEV_SAMP to STDDEV
13555        let func_name = match self.config.dialect {
13556            Some(DialectType::Snowflake) => "STDDEV",
13557            _ => "STDDEV_SAMP",
13558        };
13559
13560        self.generate_agg_func(func_name, f)
13561    }
13562
13563    fn generate_collation(&mut self, coll: &CollationExpr) -> Result<()> {
13564        self.generate_expression(&coll.this)?;
13565        self.write_space();
13566        self.write_keyword("COLLATE");
13567        self.write_space();
13568        if coll.quoted {
13569            // Single-quoted string: COLLATE 'de_DE'
13570            self.write("'");
13571            self.write(&coll.collation);
13572            self.write("'");
13573        } else if coll.double_quoted {
13574            // Double-quoted identifier: COLLATE "de_DE"
13575            self.write("\"");
13576            self.write(&coll.collation);
13577            self.write("\"");
13578        } else {
13579            // Unquoted identifier: COLLATE de_DE
13580            self.write(&coll.collation);
13581        }
13582        Ok(())
13583    }
13584
13585    fn generate_case(&mut self, case: &Case) -> Result<()> {
13586        let multiline_case = self.config.pretty
13587            && matches!(self.config.dialect, Some(DialectType::Snowflake));
13588
13589        self.write_keyword("CASE");
13590        if let Some(operand) = &case.operand {
13591            self.write_space();
13592            self.generate_expression(operand)?;
13593        }
13594        if multiline_case {
13595            self.indent_level += 1;
13596        }
13597        for (condition, result) in &case.whens {
13598            if multiline_case {
13599                self.write_newline();
13600                self.write_indent();
13601            } else {
13602                self.write_space();
13603            }
13604            self.write_keyword("WHEN");
13605            self.write_space();
13606            self.generate_expression(condition)?;
13607            if multiline_case {
13608                self.write_newline();
13609                self.write_indent();
13610            } else {
13611                self.write_space();
13612            }
13613            self.write_keyword("THEN");
13614            self.write_space();
13615            self.generate_expression(result)?;
13616        }
13617        if let Some(else_) = &case.else_ {
13618            if multiline_case {
13619                self.write_newline();
13620                self.write_indent();
13621            } else {
13622                self.write_space();
13623            }
13624            self.write_keyword("ELSE");
13625            self.write_space();
13626            self.generate_expression(else_)?;
13627        }
13628        if multiline_case {
13629            self.indent_level -= 1;
13630            self.write_newline();
13631            self.write_indent();
13632        } else {
13633            self.write_space();
13634        }
13635        self.write_keyword("END");
13636        Ok(())
13637    }
13638
13639    fn generate_function(&mut self, func: &Function) -> Result<()> {
13640        // Normalize function name based on dialect settings
13641        let normalized_name = self.normalize_func_name(&func.name);
13642        let upper_name = func.name.to_uppercase();
13643
13644        // STRUCT function: BigQuery STRUCT('Alice' AS name, 85 AS score) -> dialect-specific
13645        if upper_name == "STRUCT" && !matches!(self.config.dialect, Some(DialectType::BigQuery) | Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) | None) {
13646            return self.generate_struct_function_cross_dialect(func);
13647        }
13648
13649        // SingleStore: __SS_JSON_PATH_QMARK__(expr, key) -> expr::?key
13650        // This is an internal marker function for ::? JSON path syntax
13651        if upper_name == "__SS_JSON_PATH_QMARK__" && func.args.len() == 2 {
13652            self.generate_expression(&func.args[0])?;
13653            self.write("::?");
13654            // Extract the key from the string literal
13655            if let Expression::Literal(crate::expressions::Literal::String(key)) = &func.args[1] {
13656                self.write(key);
13657            } else {
13658                self.generate_expression(&func.args[1])?;
13659            }
13660            return Ok(());
13661        }
13662
13663        // PostgreSQL: __PG_BITWISE_XOR__(a, b) -> a # b
13664        if upper_name == "__PG_BITWISE_XOR__" && func.args.len() == 2 {
13665            self.generate_expression(&func.args[0])?;
13666            self.write(" # ");
13667            self.generate_expression(&func.args[1])?;
13668            return Ok(());
13669        }
13670
13671        // Spark/Hive family: unwrap TRY(expr) since these dialects don't emit TRY as a scalar wrapper.
13672        if matches!(
13673            self.config.dialect,
13674            Some(DialectType::Spark | DialectType::Databricks | DialectType::Hive)
13675        ) && upper_name == "TRY"
13676            && func.args.len() == 1
13677        {
13678            self.generate_expression(&func.args[0])?;
13679            return Ok(());
13680        }
13681
13682        // ClickHouse normalization: toStartOfDay(x) -> dateTrunc('DAY', x)
13683        if self.config.dialect == Some(DialectType::ClickHouse)
13684            && upper_name == "TOSTARTOFDAY"
13685            && func.args.len() == 1
13686        {
13687            self.write("dateTrunc('DAY', ");
13688            self.generate_expression(&func.args[0])?;
13689            self.write(")");
13690            return Ok(());
13691        }
13692
13693        // Redshift: CONCAT(a, b, ...) -> a || b || ...
13694        if self.config.dialect == Some(DialectType::Redshift) && upper_name == "CONCAT" && func.args.len() >= 2 {
13695            for (i, arg) in func.args.iter().enumerate() {
13696                if i > 0 {
13697                    self.write(" || ");
13698                }
13699                self.generate_expression(arg)?;
13700            }
13701            return Ok(());
13702        }
13703
13704        // Redshift: CONCAT_WS(delim, a, b, c) -> a || delim || b || delim || c
13705        if self.config.dialect == Some(DialectType::Redshift) && upper_name == "CONCAT_WS" && func.args.len() >= 2 {
13706            let sep = &func.args[0];
13707            for (i, arg) in func.args.iter().skip(1).enumerate() {
13708                if i > 0 {
13709                    self.write(" || ");
13710                    self.generate_expression(sep)?;
13711                    self.write(" || ");
13712                }
13713                self.generate_expression(arg)?;
13714            }
13715            return Ok(());
13716        }
13717
13718        // Redshift: DATEDIFF/DATE_DIFF(unit, start, end) -> DATEDIFF(UNIT, start, end)
13719        // Unit should be unquoted uppercase identifier
13720        if self.config.dialect == Some(DialectType::Redshift)
13721            && (upper_name == "DATEDIFF" || upper_name == "DATE_DIFF")
13722            && func.args.len() == 3
13723        {
13724            self.write_keyword("DATEDIFF");
13725            self.write("(");
13726            // First arg is unit - normalize to unquoted uppercase
13727            self.write_redshift_date_part(&func.args[0]);
13728            self.write(", ");
13729            self.generate_expression(&func.args[1])?;
13730            self.write(", ");
13731            self.generate_expression(&func.args[2])?;
13732            self.write(")");
13733            return Ok(());
13734        }
13735
13736        // Redshift: DATEADD/DATE_ADD(unit, interval, date) -> DATEADD(UNIT, interval, date)
13737        // Unit should be unquoted uppercase identifier
13738        if self.config.dialect == Some(DialectType::Redshift)
13739            && (upper_name == "DATEADD" || upper_name == "DATE_ADD")
13740            && func.args.len() == 3
13741        {
13742            self.write_keyword("DATEADD");
13743            self.write("(");
13744            // First arg is unit - normalize to unquoted uppercase
13745            self.write_redshift_date_part(&func.args[0]);
13746            self.write(", ");
13747            self.generate_expression(&func.args[1])?;
13748            self.write(", ");
13749            self.generate_expression(&func.args[2])?;
13750            self.write(")");
13751            return Ok(());
13752        }
13753
13754        // UUID_STRING(args) from Snowflake -> dialect-specific UUID function (dropping args)
13755        if upper_name == "UUID_STRING" && !matches!(self.config.dialect, Some(DialectType::Snowflake) | None) {
13756            let func_name = match self.config.dialect {
13757                Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => "GEN_RANDOM_UUID",
13758                Some(DialectType::BigQuery) => "GENERATE_UUID",
13759                _ => "UUID",
13760            };
13761            self.write_keyword(func_name);
13762            self.write("()");
13763            return Ok(());
13764        }
13765
13766        // Redshift: DATE_TRUNC('unit', date) -> DATE_TRUNC('UNIT', date)
13767        // Unit should be quoted uppercase string
13768        if self.config.dialect == Some(DialectType::Redshift)
13769            && upper_name == "DATE_TRUNC"
13770            && func.args.len() == 2
13771        {
13772            self.write_keyword("DATE_TRUNC");
13773            self.write("(");
13774            // First arg is unit - normalize to quoted uppercase
13775            self.write_redshift_date_part_quoted(&func.args[0]);
13776            self.write(", ");
13777            self.generate_expression(&func.args[1])?;
13778            self.write(")");
13779            return Ok(());
13780        }
13781
13782        // TSQL/Fabric: DATE_PART -> DATEPART (no underscore)
13783        if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric))
13784            && (upper_name == "DATE_PART" || upper_name == "DATEPART")
13785            && func.args.len() == 2
13786        {
13787            self.write_keyword("DATEPART");
13788            self.write("(");
13789            self.generate_expression(&func.args[0])?;
13790            self.write(", ");
13791            self.generate_expression(&func.args[1])?;
13792            self.write(")");
13793            return Ok(());
13794        }
13795
13796        // PostgreSQL/Redshift: DATE_PART(part, value) -> EXTRACT(part FROM value)
13797        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift))
13798            && (upper_name == "DATE_PART" || upper_name == "DATEPART")
13799            && func.args.len() == 2
13800        {
13801            self.write_keyword("EXTRACT");
13802            self.write("(");
13803            // Extract the datetime field - if it's a string literal, strip quotes to make it a keyword
13804            match &func.args[0] {
13805                Expression::Literal(crate::expressions::Literal::String(s)) => {
13806                    self.write(&s.to_lowercase());
13807                }
13808                _ => self.generate_expression(&func.args[0])?,
13809            }
13810            self.write_space();
13811            self.write_keyword("FROM");
13812            self.write_space();
13813            self.generate_expression(&func.args[1])?;
13814            self.write(")");
13815            return Ok(());
13816        }
13817
13818        // Dremio: DATE_PART(part, value) -> EXTRACT(part FROM value)
13819        // Also DATE literals in Dremio should be CAST(...AS DATE)
13820        if self.config.dialect == Some(DialectType::Dremio)
13821            && (upper_name == "DATE_PART" || upper_name == "DATEPART")
13822            && func.args.len() == 2
13823        {
13824            self.write_keyword("EXTRACT");
13825            self.write("(");
13826            self.generate_expression(&func.args[0])?;
13827            self.write_space();
13828            self.write_keyword("FROM");
13829            self.write_space();
13830            // For Dremio, DATE literals should become CAST('value' AS DATE)
13831            self.generate_dremio_date_expression(&func.args[1])?;
13832            self.write(")");
13833            return Ok(());
13834        }
13835
13836        // Dremio: CURRENT_DATE_UTC() -> CURRENT_DATE_UTC (no parentheses)
13837        if self.config.dialect == Some(DialectType::Dremio)
13838            && upper_name == "CURRENT_DATE_UTC"
13839            && func.args.is_empty()
13840        {
13841            self.write_keyword("CURRENT_DATE_UTC");
13842            return Ok(());
13843        }
13844
13845        // Dremio: DATETYPE(year, month, day) transformation
13846        // - If all args are integer literals: DATE('YYYY-MM-DD')
13847        // - If args are expressions: CAST(CONCAT(x, '-', y, '-', z) AS DATE)
13848        if self.config.dialect == Some(DialectType::Dremio)
13849            && upper_name == "DATETYPE"
13850            && func.args.len() == 3
13851        {
13852            // Helper function to extract integer from number literal
13853            fn get_int_literal(expr: &Expression) -> Option<i64> {
13854                if let Expression::Literal(crate::expressions::Literal::Number(s)) = expr {
13855                    s.parse::<i64>().ok()
13856                } else {
13857                    None
13858                }
13859            }
13860
13861            // Check if all arguments are integer literals
13862            if let (Some(year), Some(month), Some(day)) = (
13863                get_int_literal(&func.args[0]),
13864                get_int_literal(&func.args[1]),
13865                get_int_literal(&func.args[2]),
13866            ) {
13867                // All are integer literals: DATE('YYYY-MM-DD')
13868                self.write_keyword("DATE");
13869                self.write(&format!("('{:04}-{:02}-{:02}')", year, month, day));
13870                return Ok(());
13871            }
13872
13873            // For expressions: CAST(CONCAT(x, '-', y, '-', z) AS DATE)
13874            self.write_keyword("CAST");
13875            self.write("(");
13876            self.write_keyword("CONCAT");
13877            self.write("(");
13878            self.generate_expression(&func.args[0])?;
13879            self.write(", '-', ");
13880            self.generate_expression(&func.args[1])?;
13881            self.write(", '-', ");
13882            self.generate_expression(&func.args[2])?;
13883            self.write(")");
13884            self.write_space();
13885            self.write_keyword("AS");
13886            self.write_space();
13887            self.write_keyword("DATE");
13888            self.write(")");
13889            return Ok(());
13890        }
13891
13892        // Presto/Trino: DATE_ADD('unit', interval, date) - wrap interval in CAST(...AS BIGINT)
13893        // when it's not an integer literal
13894        let is_presto_like = matches!(
13895            self.config.dialect,
13896            Some(DialectType::Presto) | Some(DialectType::Trino)
13897        );
13898        if is_presto_like
13899            && upper_name == "DATE_ADD"
13900            && func.args.len() == 3
13901        {
13902            self.write_keyword("DATE_ADD");
13903            self.write("(");
13904            // First arg: unit (pass through as-is, e.g., 'DAY')
13905            self.generate_expression(&func.args[0])?;
13906            self.write(", ");
13907            // Second arg: interval - wrap in CAST(...AS BIGINT) if it doesn't return integer type
13908            let interval = &func.args[1];
13909            let needs_cast = !self.returns_integer_type(interval);
13910            if needs_cast {
13911                self.write_keyword("CAST");
13912                self.write("(");
13913            }
13914            self.generate_expression(interval)?;
13915            if needs_cast {
13916                self.write_space();
13917                self.write_keyword("AS");
13918                self.write_space();
13919                self.write_keyword("BIGINT");
13920                self.write(")");
13921            }
13922            self.write(", ");
13923            // Third arg: date
13924            self.generate_expression(&func.args[2])?;
13925            self.write(")");
13926            return Ok(());
13927        }
13928
13929        // Use bracket syntax if the function was parsed with brackets (e.g., MAP[keys, values])
13930        let use_brackets = func.use_bracket_syntax;
13931
13932        // Special case: functions WITH ORDINALITY need special output order
13933        // Input: FUNC(args) WITH ORDINALITY
13934        // Stored as: name="FUNC WITH ORDINALITY", args=[...]
13935        // Output must be: FUNC(args) WITH ORDINALITY
13936        let has_ordinality = upper_name.ends_with(" WITH ORDINALITY");
13937        let output_name = if has_ordinality {
13938            let base_name = &func.name[..func.name.len() - " WITH ORDINALITY".len()];
13939            self.normalize_func_name(base_name)
13940        } else {
13941            normalized_name.clone()
13942        };
13943
13944        // For qualified names (schema.function or object.method), preserve original case
13945        // because they can be case-sensitive (e.g., TSQL XML methods like .nodes(), .value())
13946        if func.name.contains('.') && !has_ordinality {
13947            // Don't normalize qualified functions - preserve original case
13948            // If the function was quoted (e.g., BigQuery `p.d.UdF`), wrap it in backticks
13949            if func.quoted {
13950                self.write("`");
13951                self.write(&func.name);
13952                self.write("`");
13953            } else {
13954                self.write(&func.name);
13955            }
13956        } else {
13957            self.write(&output_name);
13958        }
13959
13960        // If no_parens is true and there are no args, output just the function name
13961        // Unless the target dialect requires parens for this function
13962        let force_parens = func.no_parens && func.args.is_empty() && !func.distinct && {
13963            let needs_parens = match upper_name.as_str() {
13964                "CURRENT_USER" | "SESSION_USER" | "SYSTEM_USER" => matches!(
13965                    self.config.dialect,
13966                    Some(DialectType::Snowflake) | Some(DialectType::Spark)
13967                    | Some(DialectType::Databricks) | Some(DialectType::Hive)
13968                ),
13969                _ => false,
13970            };
13971            !needs_parens
13972        };
13973        if force_parens {
13974            // Output trailing comments
13975            for comment in &func.trailing_comments {
13976                self.write_space();
13977                self.write(comment);
13978            }
13979            return Ok(());
13980        }
13981
13982        // CUBE, ROLLUP, GROUPING SETS need a space before the parenthesis
13983        if upper_name == "CUBE" || upper_name == "ROLLUP" || upper_name == "GROUPING SETS" {
13984            self.write(" (");
13985        } else if use_brackets {
13986            self.write("[");
13987        } else {
13988            self.write("(");
13989        }
13990        if func.distinct {
13991            self.write_keyword("DISTINCT");
13992            self.write_space();
13993        }
13994
13995        // Check if arguments should be split onto multiple lines (pretty + too wide)
13996        let compact_pretty_func = matches!(self.config.dialect, Some(DialectType::Snowflake))
13997            && (upper_name == "TABLE" || upper_name == "FLATTEN");
13998        let should_split = if self.config.pretty && !func.args.is_empty() && !compact_pretty_func {
13999            // Pre-render arguments to check total width
14000            let mut expr_strings: Vec<String> = Vec::with_capacity(func.args.len());
14001            for arg in &func.args {
14002                let mut temp_gen = Generator::with_config(self.config.clone());
14003                temp_gen.config.pretty = false; // Don't recurse into pretty
14004                temp_gen.generate_expression(arg)?;
14005                expr_strings.push(temp_gen.output);
14006            }
14007            self.too_wide(&expr_strings)
14008        } else {
14009            false
14010        };
14011
14012        if should_split {
14013            // Split onto multiple lines
14014            self.write_newline();
14015            self.indent_level += 1;
14016            for (i, arg) in func.args.iter().enumerate() {
14017                self.write_indent();
14018                self.generate_expression(arg)?;
14019                if i + 1 < func.args.len() {
14020                    self.write(",");
14021                }
14022                self.write_newline();
14023            }
14024            self.indent_level -= 1;
14025            self.write_indent();
14026        } else {
14027            // All on one line
14028            for (i, arg) in func.args.iter().enumerate() {
14029                if i > 0 {
14030                    self.write(", ");
14031                }
14032                self.generate_expression(arg)?;
14033            }
14034        }
14035
14036        if use_brackets {
14037            self.write("]");
14038        } else {
14039            self.write(")");
14040        }
14041        // Append WITH ORDINALITY after closing paren for table-valued functions
14042        if has_ordinality {
14043            self.write_space();
14044            self.write_keyword("WITH ORDINALITY");
14045        }
14046        // Output trailing comments
14047        for comment in &func.trailing_comments {
14048            self.write_space();
14049            self.write(comment);
14050        }
14051        Ok(())
14052    }
14053
14054    fn generate_aggregate_function(&mut self, func: &AggregateFunction) -> Result<()> {
14055        // Normalize function name based on dialect settings
14056        let mut normalized_name = self.normalize_func_name(&func.name);
14057
14058        // Dialect-specific name mappings for aggregate functions
14059        let upper = normalized_name.to_uppercase();
14060        if upper == "MAX_BY" || upper == "MIN_BY" {
14061            let is_max = upper == "MAX_BY";
14062            match self.config.dialect {
14063                Some(DialectType::ClickHouse) => {
14064                    normalized_name = if is_max { "argMax".to_string() } else { "argMin".to_string() };
14065                }
14066                Some(DialectType::DuckDB) => {
14067                    normalized_name = if is_max { "ARG_MAX".to_string() } else { "ARG_MIN".to_string() };
14068                }
14069                _ => {}
14070            }
14071        }
14072        self.write(&normalized_name);
14073        self.write("(");
14074        if func.distinct {
14075            self.write_keyword("DISTINCT");
14076            self.write_space();
14077        }
14078
14079        // Check if we need to transform multi-arg COUNT DISTINCT
14080        // When dialect doesn't support multi_arg_distinct, transform:
14081        // COUNT(DISTINCT a, b) -> COUNT(DISTINCT CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE (a, b) END)
14082        let is_count = normalized_name.eq_ignore_ascii_case("COUNT");
14083        let needs_multi_arg_transform = func.distinct
14084            && is_count
14085            && func.args.len() > 1
14086            && !self.config.multi_arg_distinct;
14087
14088        if needs_multi_arg_transform {
14089            // Generate: CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE (a, b) END
14090            self.write_keyword("CASE");
14091            for arg in &func.args {
14092                self.write_space();
14093                self.write_keyword("WHEN");
14094                self.write_space();
14095                self.generate_expression(arg)?;
14096                self.write_space();
14097                self.write_keyword("IS NULL THEN NULL");
14098            }
14099            self.write_space();
14100            self.write_keyword("ELSE");
14101            self.write(" (");
14102            for (i, arg) in func.args.iter().enumerate() {
14103                if i > 0 {
14104                    self.write(", ");
14105                }
14106                self.generate_expression(arg)?;
14107            }
14108            self.write(")");
14109            self.write_space();
14110            self.write_keyword("END");
14111        } else {
14112            for (i, arg) in func.args.iter().enumerate() {
14113                if i > 0 {
14114                    self.write(", ");
14115                }
14116                self.generate_expression(arg)?;
14117            }
14118        }
14119
14120        // IGNORE NULLS / RESPECT NULLS inside parens (for BigQuery style or when config says in_func)
14121        if self.config.ignore_nulls_in_func && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
14122            if let Some(ignore) = func.ignore_nulls {
14123                self.write_space();
14124                if ignore {
14125                    self.write_keyword("IGNORE NULLS");
14126                } else {
14127                    self.write_keyword("RESPECT NULLS");
14128                }
14129            }
14130        }
14131
14132        // ORDER BY inside aggregate
14133        if !func.order_by.is_empty() {
14134            self.write_space();
14135            self.write_keyword("ORDER BY");
14136            self.write_space();
14137            for (i, ord) in func.order_by.iter().enumerate() {
14138                if i > 0 {
14139                    self.write(", ");
14140                }
14141                self.generate_ordered(ord)?;
14142            }
14143        }
14144
14145        // LIMIT inside aggregate
14146        if let Some(limit) = &func.limit {
14147            self.write_space();
14148            self.write_keyword("LIMIT");
14149            self.write_space();
14150            // Check if this is a Tuple representing LIMIT offset, count
14151            if let Expression::Tuple(t) = limit.as_ref() {
14152                if t.expressions.len() == 2 {
14153                    self.generate_expression(&t.expressions[0])?;
14154                    self.write(", ");
14155                    self.generate_expression(&t.expressions[1])?;
14156                } else {
14157                    self.generate_expression(limit)?;
14158                }
14159            } else {
14160                self.generate_expression(limit)?;
14161            }
14162        }
14163
14164        self.write(")");
14165
14166        // IGNORE NULLS / RESPECT NULLS outside parens (standard style)
14167        if !self.config.ignore_nulls_in_func && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
14168            if let Some(ignore) = func.ignore_nulls {
14169                self.write_space();
14170                if ignore {
14171                    self.write_keyword("IGNORE NULLS");
14172                } else {
14173                    self.write_keyword("RESPECT NULLS");
14174                }
14175            }
14176        }
14177
14178        if let Some(filter) = &func.filter {
14179            self.write_space();
14180            self.write_keyword("FILTER");
14181            self.write("(");
14182            self.write_keyword("WHERE");
14183            self.write_space();
14184            self.generate_expression(filter)?;
14185            self.write(")");
14186        }
14187
14188        Ok(())
14189    }
14190
14191    fn generate_window_function(&mut self, wf: &WindowFunction) -> Result<()> {
14192        self.generate_expression(&wf.this)?;
14193
14194        // Generate KEEP clause if present (Oracle KEEP (DENSE_RANK FIRST|LAST ORDER BY ...))
14195        if let Some(keep) = &wf.keep {
14196            self.write_space();
14197            self.write_keyword("KEEP");
14198            self.write(" (");
14199            self.write_keyword("DENSE_RANK");
14200            self.write_space();
14201            if keep.first {
14202                self.write_keyword("FIRST");
14203            } else {
14204                self.write_keyword("LAST");
14205            }
14206            self.write_space();
14207            self.write_keyword("ORDER BY");
14208            self.write_space();
14209            for (i, ord) in keep.order_by.iter().enumerate() {
14210                if i > 0 {
14211                    self.write(", ");
14212                }
14213                self.generate_ordered(ord)?;
14214            }
14215            self.write(")");
14216        }
14217
14218        // Check if there's any OVER clause content
14219        let has_over = !wf.over.partition_by.is_empty()
14220            || !wf.over.order_by.is_empty()
14221            || wf.over.frame.is_some()
14222            || wf.over.window_name.is_some();
14223
14224        // Only output OVER if there's actual window specification (not just KEEP alone)
14225        if has_over {
14226            self.write_space();
14227            self.write_keyword("OVER");
14228
14229            // Check if this is just a bare named window reference (no parens needed)
14230            let has_specs = !wf.over.partition_by.is_empty()
14231                || !wf.over.order_by.is_empty()
14232                || wf.over.frame.is_some();
14233
14234            if wf.over.window_name.is_some() && !has_specs {
14235                // OVER window_name (without parentheses)
14236                self.write_space();
14237                self.write(&wf.over.window_name.as_ref().unwrap().name);
14238            } else {
14239                // OVER (...) or OVER (window_name ...)
14240                self.write(" (");
14241                self.generate_over(&wf.over)?;
14242                self.write(")");
14243            }
14244        } else if wf.keep.is_none() {
14245            // No KEEP and no OVER content, but still a WindowFunction - output empty OVER ()
14246            self.write_space();
14247            self.write_keyword("OVER");
14248            self.write(" ()");
14249        }
14250
14251        Ok(())
14252    }
14253
14254    /// Generate WITHIN GROUP clause (for ordered-set aggregate functions)
14255    fn generate_within_group(&mut self, wg: &WithinGroup) -> Result<()> {
14256        self.generate_expression(&wg.this)?;
14257        self.write_space();
14258        self.write_keyword("WITHIN GROUP");
14259        self.write(" (");
14260        self.write_keyword("ORDER BY");
14261        self.write_space();
14262        for (i, ord) in wg.order_by.iter().enumerate() {
14263            if i > 0 {
14264                self.write(", ");
14265            }
14266            self.generate_ordered(ord)?;
14267        }
14268        self.write(")");
14269        Ok(())
14270    }
14271
14272    /// Generate the contents of an OVER clause (without parentheses)
14273    fn generate_over(&mut self, over: &Over) -> Result<()> {
14274        let mut has_content = false;
14275
14276        // Named window reference
14277        if let Some(name) = &over.window_name {
14278            self.write(&name.name);
14279            has_content = true;
14280        }
14281
14282        // PARTITION BY
14283        if !over.partition_by.is_empty() {
14284            if has_content {
14285                self.write_space();
14286            }
14287            self.write_keyword("PARTITION BY");
14288            self.write_space();
14289            for (i, expr) in over.partition_by.iter().enumerate() {
14290                if i > 0 {
14291                    self.write(", ");
14292                }
14293                self.generate_expression(expr)?;
14294            }
14295            has_content = true;
14296        }
14297
14298        // ORDER BY
14299        if !over.order_by.is_empty() {
14300            if has_content {
14301                self.write_space();
14302            }
14303            self.write_keyword("ORDER BY");
14304            self.write_space();
14305            for (i, ordered) in over.order_by.iter().enumerate() {
14306                if i > 0 {
14307                    self.write(", ");
14308                }
14309                self.generate_ordered(ordered)?;
14310            }
14311            has_content = true;
14312        }
14313
14314        // Window frame
14315        if let Some(frame) = &over.frame {
14316            if has_content {
14317                self.write_space();
14318            }
14319            self.generate_window_frame(frame)?;
14320        }
14321
14322        Ok(())
14323    }
14324
14325    fn generate_window_frame(&mut self, frame: &WindowFrame) -> Result<()> {
14326        // Exasol uses lowercase for frame kind (rows/range/groups)
14327        let lowercase_frame = self.config.lowercase_window_frame_keywords;
14328
14329        // Use preserved kind_text if available (for case preservation), unless lowercase override is active
14330        if !lowercase_frame {
14331            if let Some(kind_text) = &frame.kind_text {
14332                self.write(kind_text);
14333            } else {
14334                match frame.kind {
14335                    WindowFrameKind::Rows => self.write_keyword("ROWS"),
14336                    WindowFrameKind::Range => self.write_keyword("RANGE"),
14337                    WindowFrameKind::Groups => self.write_keyword("GROUPS"),
14338                }
14339            }
14340        } else {
14341            match frame.kind {
14342                WindowFrameKind::Rows => self.write("rows"),
14343                WindowFrameKind::Range => self.write("range"),
14344                WindowFrameKind::Groups => self.write("groups"),
14345            }
14346        }
14347
14348        // Use BETWEEN format only when there's an explicit end bound,
14349        // or when normalize_window_frame_between is enabled and the start is a directional bound
14350        self.write_space();
14351        let should_normalize = self.config.normalize_window_frame_between
14352            && frame.end.is_none()
14353            && matches!(
14354                frame.start,
14355                WindowFrameBound::Preceding(_)
14356                    | WindowFrameBound::Following(_)
14357                    | WindowFrameBound::UnboundedPreceding
14358                    | WindowFrameBound::UnboundedFollowing
14359            );
14360
14361        if let Some(end) = &frame.end {
14362            // BETWEEN format: RANGE BETWEEN start AND end
14363            self.write_keyword("BETWEEN");
14364            self.write_space();
14365            self.generate_window_frame_bound(&frame.start, frame.start_side_text.as_deref())?;
14366            self.write_space();
14367            self.write_keyword("AND");
14368            self.write_space();
14369            self.generate_window_frame_bound(end, frame.end_side_text.as_deref())?;
14370        } else if should_normalize {
14371            // Normalize single-bound to BETWEEN form: ROWS 1 PRECEDING → ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
14372            self.write_keyword("BETWEEN");
14373            self.write_space();
14374            self.generate_window_frame_bound(&frame.start, frame.start_side_text.as_deref())?;
14375            self.write_space();
14376            self.write_keyword("AND");
14377            self.write_space();
14378            self.write_keyword("CURRENT ROW");
14379        } else {
14380            // Single bound format: RANGE CURRENT ROW
14381            self.generate_window_frame_bound(&frame.start, frame.start_side_text.as_deref())?;
14382        }
14383
14384        // EXCLUDE clause
14385        if let Some(exclude) = &frame.exclude {
14386            self.write_space();
14387            self.write_keyword("EXCLUDE");
14388            self.write_space();
14389            match exclude {
14390                WindowFrameExclude::CurrentRow => self.write_keyword("CURRENT ROW"),
14391                WindowFrameExclude::Group => self.write_keyword("GROUP"),
14392                WindowFrameExclude::Ties => self.write_keyword("TIES"),
14393                WindowFrameExclude::NoOthers => self.write_keyword("NO OTHERS"),
14394            }
14395        }
14396
14397        Ok(())
14398    }
14399
14400    fn generate_window_frame_bound(&mut self, bound: &WindowFrameBound, side_text: Option<&str>) -> Result<()> {
14401        // Exasol uses lowercase for preceding/following
14402        let lowercase_frame = self.config.lowercase_window_frame_keywords;
14403
14404        match bound {
14405            WindowFrameBound::CurrentRow => {
14406                self.write_keyword("CURRENT ROW");
14407            }
14408            WindowFrameBound::UnboundedPreceding => {
14409                self.write_keyword("UNBOUNDED");
14410                self.write_space();
14411                if lowercase_frame {
14412                    self.write("preceding");
14413                } else if let Some(text) = side_text {
14414                    self.write(text);
14415                } else {
14416                    self.write_keyword("PRECEDING");
14417                }
14418            }
14419            WindowFrameBound::UnboundedFollowing => {
14420                self.write_keyword("UNBOUNDED");
14421                self.write_space();
14422                if lowercase_frame {
14423                    self.write("following");
14424                } else if let Some(text) = side_text {
14425                    self.write(text);
14426                } else {
14427                    self.write_keyword("FOLLOWING");
14428                }
14429            }
14430            WindowFrameBound::Preceding(expr) => {
14431                self.generate_expression(expr)?;
14432                self.write_space();
14433                if lowercase_frame {
14434                    self.write("preceding");
14435                } else if let Some(text) = side_text {
14436                    self.write(text);
14437                } else {
14438                    self.write_keyword("PRECEDING");
14439                }
14440            }
14441            WindowFrameBound::Following(expr) => {
14442                self.generate_expression(expr)?;
14443                self.write_space();
14444                if lowercase_frame {
14445                    self.write("following");
14446                } else if let Some(text) = side_text {
14447                    self.write(text);
14448                } else {
14449                    self.write_keyword("FOLLOWING");
14450                }
14451            }
14452            WindowFrameBound::BarePreceding => {
14453                if lowercase_frame {
14454                    self.write("preceding");
14455                } else if let Some(text) = side_text {
14456                    self.write(text);
14457                } else {
14458                    self.write_keyword("PRECEDING");
14459                }
14460            }
14461            WindowFrameBound::BareFollowing => {
14462                if lowercase_frame {
14463                    self.write("following");
14464                } else if let Some(text) = side_text {
14465                    self.write(text);
14466                } else {
14467                    self.write_keyword("FOLLOWING");
14468                }
14469            }
14470            WindowFrameBound::Value(expr) => {
14471                // Bare numeric bound without PRECEDING/FOLLOWING
14472                self.generate_expression(expr)?;
14473            }
14474        }
14475        Ok(())
14476    }
14477
14478    fn generate_interval(&mut self, interval: &Interval) -> Result<()> {
14479        // For Oracle with ExprSpan: only output INTERVAL if `this` is a literal
14480        // (e.g., `(expr) DAY(9) TO SECOND(3)` should NOT have INTERVAL prefix)
14481        let skip_interval_keyword = matches!(self.config.dialect, Some(DialectType::Oracle))
14482            && matches!(&interval.unit, Some(IntervalUnitSpec::ExprSpan(_)))
14483            && !matches!(&interval.this, Some(Expression::Literal(_)));
14484
14485        if !skip_interval_keyword {
14486            self.write_keyword("INTERVAL");
14487        }
14488
14489        // Generate value if present
14490        if let Some(ref value) = interval.this {
14491            if !skip_interval_keyword {
14492                self.write_space();
14493            }
14494            // If the value is a complex expression (not a literal/column/function call)
14495            // and there's a unit, wrap it in parentheses
14496            // e.g., INTERVAL (2 * 2) MONTH, INTERVAL (DAYOFMONTH(dt) - 1) DAY
14497            let needs_parens = interval.unit.is_some() && matches!(value,
14498                Expression::Add(_) | Expression::Sub(_) | Expression::Mul(_) |
14499                Expression::Div(_) | Expression::Mod(_) | Expression::BitwiseAnd(_) |
14500                Expression::BitwiseOr(_) | Expression::BitwiseXor(_)
14501            );
14502            if needs_parens {
14503                self.write("(");
14504            }
14505            self.generate_expression(value)?;
14506            if needs_parens {
14507                self.write(")");
14508            }
14509        }
14510
14511        // Generate unit if present
14512        if let Some(ref unit_spec) = interval.unit {
14513            self.write_space();
14514            self.write_interval_unit_spec(unit_spec)?;
14515        }
14516
14517        Ok(())
14518    }
14519
14520    fn write_interval_unit_spec(&mut self, unit_spec: &IntervalUnitSpec) -> Result<()> {
14521        match unit_spec {
14522            IntervalUnitSpec::Simple { unit, use_plural } => {
14523                // If dialect doesn't allow plural forms, force singular
14524                let effective_plural = *use_plural && self.config.interval_allows_plural_form;
14525                self.write_simple_interval_unit(unit, effective_plural);
14526            }
14527            IntervalUnitSpec::Span(span) => {
14528                self.write_simple_interval_unit(&span.this, false);
14529                self.write_space();
14530                self.write_keyword("TO");
14531                self.write_space();
14532                self.write_simple_interval_unit(&span.expression, false);
14533            }
14534            IntervalUnitSpec::ExprSpan(span) => {
14535                // Expression-based interval span (e.g., DAY(9) TO SECOND(3))
14536                self.generate_expression(&span.this)?;
14537                self.write_space();
14538                self.write_keyword("TO");
14539                self.write_space();
14540                self.generate_expression(&span.expression)?;
14541            }
14542            IntervalUnitSpec::Expr(expr) => {
14543                self.generate_expression(expr)?;
14544            }
14545        }
14546        Ok(())
14547    }
14548
14549    fn write_simple_interval_unit(&mut self, unit: &IntervalUnit, use_plural: bool) {
14550        // Output interval unit, respecting plural preference
14551        match (unit, use_plural) {
14552            (IntervalUnit::Year, false) => self.write_keyword("YEAR"),
14553            (IntervalUnit::Year, true) => self.write_keyword("YEARS"),
14554            (IntervalUnit::Quarter, false) => self.write_keyword("QUARTER"),
14555            (IntervalUnit::Quarter, true) => self.write_keyword("QUARTERS"),
14556            (IntervalUnit::Month, false) => self.write_keyword("MONTH"),
14557            (IntervalUnit::Month, true) => self.write_keyword("MONTHS"),
14558            (IntervalUnit::Week, false) => self.write_keyword("WEEK"),
14559            (IntervalUnit::Week, true) => self.write_keyword("WEEKS"),
14560            (IntervalUnit::Day, false) => self.write_keyword("DAY"),
14561            (IntervalUnit::Day, true) => self.write_keyword("DAYS"),
14562            (IntervalUnit::Hour, false) => self.write_keyword("HOUR"),
14563            (IntervalUnit::Hour, true) => self.write_keyword("HOURS"),
14564            (IntervalUnit::Minute, false) => self.write_keyword("MINUTE"),
14565            (IntervalUnit::Minute, true) => self.write_keyword("MINUTES"),
14566            (IntervalUnit::Second, false) => self.write_keyword("SECOND"),
14567            (IntervalUnit::Second, true) => self.write_keyword("SECONDS"),
14568            (IntervalUnit::Millisecond, false) => self.write_keyword("MILLISECOND"),
14569            (IntervalUnit::Millisecond, true) => self.write_keyword("MILLISECONDS"),
14570            (IntervalUnit::Microsecond, false) => self.write_keyword("MICROSECOND"),
14571            (IntervalUnit::Microsecond, true) => self.write_keyword("MICROSECONDS"),
14572        }
14573    }
14574
14575    /// Normalize a date part expression to unquoted uppercase for Redshift DATEDIFF/DATEADD
14576    /// Converts: 'day', 'days', day, days, DAY -> DAY (unquoted)
14577    fn write_redshift_date_part(&mut self, expr: &Expression) {
14578        let part_str = self.extract_date_part_string(expr);
14579        if let Some(part) = part_str {
14580            let normalized = self.normalize_date_part(&part);
14581            self.write_keyword(&normalized);
14582        } else {
14583            // If we can't extract a date part string, fall back to generating the expression
14584            let _ = self.generate_expression(expr);
14585        }
14586    }
14587
14588    /// Normalize a date part expression to quoted uppercase for Redshift DATE_TRUNC
14589    /// Converts: 'day', day, DAY -> 'DAY' (quoted)
14590    fn write_redshift_date_part_quoted(&mut self, expr: &Expression) {
14591        let part_str = self.extract_date_part_string(expr);
14592        if let Some(part) = part_str {
14593            let normalized = self.normalize_date_part(&part);
14594            self.write("'");
14595            self.write(&normalized);
14596            self.write("'");
14597        } else {
14598            // If we can't extract a date part string, fall back to generating the expression
14599            let _ = self.generate_expression(expr);
14600        }
14601    }
14602
14603    /// Extract date part string from expression (handles string literals and identifiers)
14604    fn extract_date_part_string(&self, expr: &Expression) -> Option<String> {
14605        match expr {
14606            Expression::Literal(crate::expressions::Literal::String(s)) => Some(s.clone()),
14607            Expression::Identifier(id) => Some(id.name.clone()),
14608            Expression::Column(col) if col.table.is_none() => {
14609                // Simple column reference without table prefix, treat as identifier
14610                Some(col.name.name.clone())
14611            }
14612            _ => None,
14613        }
14614    }
14615
14616    /// Normalize date part to uppercase singular form
14617    /// days -> DAY, months -> MONTH, etc.
14618    fn normalize_date_part(&self, part: &str) -> String {
14619        let lower = part.to_lowercase();
14620        match lower.as_str() {
14621            "day" | "days" | "d" => "DAY".to_string(),
14622            "month" | "months" | "mon" | "mm" => "MONTH".to_string(),
14623            "year" | "years" | "y" | "yy" | "yyyy" => "YEAR".to_string(),
14624            "week" | "weeks" | "w" | "wk" => "WEEK".to_string(),
14625            "hour" | "hours" | "h" | "hh" => "HOUR".to_string(),
14626            "minute" | "minutes" | "m" | "mi" | "n" => "MINUTE".to_string(),
14627            "second" | "seconds" | "s" | "ss" => "SECOND".to_string(),
14628            "millisecond" | "milliseconds" | "ms" => "MILLISECOND".to_string(),
14629            "microsecond" | "microseconds" | "us" => "MICROSECOND".to_string(),
14630            "quarter" | "quarters" | "q" | "qq" => "QUARTER".to_string(),
14631            _ => part.to_uppercase(),
14632        }
14633    }
14634
14635    fn write_datetime_field(&mut self, field: &DateTimeField) {
14636        match field {
14637            DateTimeField::Year => self.write_keyword("YEAR"),
14638            DateTimeField::Month => self.write_keyword("MONTH"),
14639            DateTimeField::Day => self.write_keyword("DAY"),
14640            DateTimeField::Hour => self.write_keyword("HOUR"),
14641            DateTimeField::Minute => self.write_keyword("MINUTE"),
14642            DateTimeField::Second => self.write_keyword("SECOND"),
14643            DateTimeField::Millisecond => self.write_keyword("MILLISECOND"),
14644            DateTimeField::Microsecond => self.write_keyword("MICROSECOND"),
14645            DateTimeField::DayOfWeek => {
14646                let name = match self.config.dialect {
14647                    Some(DialectType::DuckDB) | Some(DialectType::Snowflake) => "DAYOFWEEK",
14648                    _ => "DOW",
14649                };
14650                self.write_keyword(name);
14651            }
14652            DateTimeField::DayOfYear => {
14653                let name = match self.config.dialect {
14654                    Some(DialectType::DuckDB) | Some(DialectType::Snowflake) => "DAYOFYEAR",
14655                    _ => "DOY",
14656                };
14657                self.write_keyword(name);
14658            }
14659            DateTimeField::Week => self.write_keyword("WEEK"),
14660            DateTimeField::WeekWithModifier(modifier) => {
14661                self.write_keyword("WEEK");
14662                self.write("(");
14663                self.write(modifier);
14664                self.write(")");
14665            }
14666            DateTimeField::Quarter => self.write_keyword("QUARTER"),
14667            DateTimeField::Epoch => self.write_keyword("EPOCH"),
14668            DateTimeField::Timezone => self.write_keyword("TIMEZONE"),
14669            DateTimeField::TimezoneHour => self.write_keyword("TIMEZONE_HOUR"),
14670            DateTimeField::TimezoneMinute => self.write_keyword("TIMEZONE_MINUTE"),
14671            DateTimeField::Date => self.write_keyword("DATE"),
14672            DateTimeField::Time => self.write_keyword("TIME"),
14673            DateTimeField::Custom(name) => self.write(name),
14674        }
14675    }
14676
14677    /// Write datetime field in lowercase (for Spark/Hive/Databricks)
14678    fn write_datetime_field_lower(&mut self, field: &DateTimeField) {
14679        match field {
14680            DateTimeField::Year => self.write("year"),
14681            DateTimeField::Month => self.write("month"),
14682            DateTimeField::Day => self.write("day"),
14683            DateTimeField::Hour => self.write("hour"),
14684            DateTimeField::Minute => self.write("minute"),
14685            DateTimeField::Second => self.write("second"),
14686            DateTimeField::Millisecond => self.write("millisecond"),
14687            DateTimeField::Microsecond => self.write("microsecond"),
14688            DateTimeField::DayOfWeek => self.write("dow"),
14689            DateTimeField::DayOfYear => self.write("doy"),
14690            DateTimeField::Week => self.write("week"),
14691            DateTimeField::WeekWithModifier(modifier) => {
14692                self.write("week(");
14693                self.write(modifier);
14694                self.write(")");
14695            }
14696            DateTimeField::Quarter => self.write("quarter"),
14697            DateTimeField::Epoch => self.write("epoch"),
14698            DateTimeField::Timezone => self.write("timezone"),
14699            DateTimeField::TimezoneHour => self.write("timezone_hour"),
14700            DateTimeField::TimezoneMinute => self.write("timezone_minute"),
14701            DateTimeField::Date => self.write("date"),
14702            DateTimeField::Time => self.write("time"),
14703            DateTimeField::Custom(name) => self.write(name),
14704        }
14705    }
14706
14707    // Helper function generators
14708
14709    fn generate_simple_func(&mut self, name: &str, arg: &Expression) -> Result<()> {
14710        self.write_keyword(name);
14711        self.write("(");
14712        self.generate_expression(arg)?;
14713        self.write(")");
14714        Ok(())
14715    }
14716
14717    /// Generate a unary function, using the original name if available for round-trip preservation
14718    fn generate_unary_func(&mut self, default_name: &str, f: &crate::expressions::UnaryFunc) -> Result<()> {
14719        let name = f.original_name.as_deref().unwrap_or(default_name);
14720        self.write_keyword(name);
14721        self.write("(");
14722        self.generate_expression(&f.this)?;
14723        self.write(")");
14724        Ok(())
14725    }
14726
14727    /// Generate SQRT/CBRT - always use function form (matches Python SQLGlot normalization)
14728    fn generate_sqrt_cbrt(&mut self, f: &crate::expressions::UnaryFunc, func_name: &str, _op: &str) -> Result<()> {
14729        // Python SQLGlot normalizes |/ and ||/ to SQRT() and CBRT()
14730        // Always use function syntax for consistency
14731        self.write_keyword(func_name);
14732        self.write("(");
14733        self.generate_expression(&f.this)?;
14734        self.write(")");
14735        Ok(())
14736    }
14737
14738    fn generate_binary_func(&mut self, name: &str, arg1: &Expression, arg2: &Expression) -> Result<()> {
14739        self.write_keyword(name);
14740        self.write("(");
14741        self.generate_expression(arg1)?;
14742        self.write(", ");
14743        self.generate_expression(arg2)?;
14744        self.write(")");
14745        Ok(())
14746    }
14747
14748    /// Generate CHAR/CHR function with optional USING charset
14749    /// e.g., CHAR(77, 77.3, '77.3' USING utf8mb4)
14750    /// e.g., CHR(187 USING NCHAR_CS) -- Oracle
14751    fn generate_char_func(&mut self, f: &crate::expressions::CharFunc) -> Result<()> {
14752        // Use stored name if available, otherwise default to CHAR
14753        let func_name = f.name.as_deref().unwrap_or("CHAR");
14754        self.write_keyword(func_name);
14755        self.write("(");
14756        for (i, arg) in f.args.iter().enumerate() {
14757            if i > 0 {
14758                self.write(", ");
14759            }
14760            self.generate_expression(arg)?;
14761        }
14762        if let Some(ref charset) = f.charset {
14763            self.write(" ");
14764            self.write_keyword("USING");
14765            self.write(" ");
14766            self.write(charset);
14767        }
14768        self.write(")");
14769        Ok(())
14770    }
14771
14772    fn generate_power(&mut self, f: &BinaryFunc) -> Result<()> {
14773        use crate::dialects::DialectType;
14774
14775        match self.config.dialect {
14776            Some(DialectType::Teradata) => {
14777                // Teradata uses ** operator for exponentiation
14778                self.generate_expression(&f.this)?;
14779                self.write(" ** ");
14780                self.generate_expression(&f.expression)?;
14781                Ok(())
14782            }
14783            _ => {
14784                // Other dialects use POWER function
14785                self.generate_binary_func("POWER", &f.this, &f.expression)
14786            }
14787        }
14788    }
14789
14790    fn generate_vararg_func(&mut self, name: &str, args: &[Expression]) -> Result<()> {
14791        self.write_keyword(name);
14792        self.write("(");
14793        for (i, arg) in args.iter().enumerate() {
14794            if i > 0 {
14795                self.write(", ");
14796            }
14797            self.generate_expression(arg)?;
14798        }
14799        self.write(")");
14800        Ok(())
14801    }
14802
14803    // String function generators
14804
14805    fn generate_concat_ws(&mut self, f: &ConcatWs) -> Result<()> {
14806        self.write_keyword("CONCAT_WS");
14807        self.write("(");
14808        self.generate_expression(&f.separator)?;
14809        for expr in &f.expressions {
14810            self.write(", ");
14811            self.generate_expression(expr)?;
14812        }
14813        self.write(")");
14814        Ok(())
14815    }
14816
14817    fn generate_substring(&mut self, f: &SubstringFunc) -> Result<()> {
14818        self.write_keyword("SUBSTRING");
14819        self.write("(");
14820        self.generate_expression(&f.this)?;
14821        // Spark/Hive use comma syntax, not FROM/FOR syntax
14822        let use_comma_syntax = matches!(
14823            self.config.dialect,
14824            Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)
14825        );
14826        if f.from_for_syntax && !use_comma_syntax {
14827            // SQL standard syntax: SUBSTRING(str FROM pos FOR len)
14828            self.write_space();
14829            self.write_keyword("FROM");
14830            self.write_space();
14831            self.generate_expression(&f.start)?;
14832            if let Some(length) = &f.length {
14833                self.write_space();
14834                self.write_keyword("FOR");
14835                self.write_space();
14836                self.generate_expression(length)?;
14837            }
14838        } else {
14839            // Comma-separated syntax: SUBSTRING(str, pos, len)
14840            self.write(", ");
14841            self.generate_expression(&f.start)?;
14842            if let Some(length) = &f.length {
14843                self.write(", ");
14844                self.generate_expression(length)?;
14845            }
14846        }
14847        self.write(")");
14848        Ok(())
14849    }
14850
14851    fn generate_overlay(&mut self, f: &OverlayFunc) -> Result<()> {
14852        self.write_keyword("OVERLAY");
14853        self.write("(");
14854        self.generate_expression(&f.this)?;
14855        self.write_space();
14856        self.write_keyword("PLACING");
14857        self.write_space();
14858        self.generate_expression(&f.replacement)?;
14859        self.write_space();
14860        self.write_keyword("FROM");
14861        self.write_space();
14862        self.generate_expression(&f.from)?;
14863        if let Some(length) = &f.length {
14864            self.write_space();
14865            self.write_keyword("FOR");
14866            self.write_space();
14867            self.generate_expression(length)?;
14868        }
14869        self.write(")");
14870        Ok(())
14871    }
14872
14873    fn generate_trim(&mut self, f: &TrimFunc) -> Result<()> {
14874        // Special case: TRIM(LEADING str) -> LTRIM(str), TRIM(TRAILING str) -> RTRIM(str)
14875        // when no characters are specified (PostgreSQL style)
14876        if f.position_explicit && f.characters.is_none() {
14877            match f.position {
14878                TrimPosition::Leading => {
14879                    self.write_keyword("LTRIM");
14880                    self.write("(");
14881                    self.generate_expression(&f.this)?;
14882                    self.write(")");
14883                    return Ok(());
14884                }
14885                TrimPosition::Trailing => {
14886                    self.write_keyword("RTRIM");
14887                    self.write("(");
14888                    self.generate_expression(&f.this)?;
14889                    self.write(")");
14890                    return Ok(());
14891                }
14892                TrimPosition::Both => {
14893                    // TRIM(BOTH str) -> BTRIM(str) in PostgreSQL, but TRIM(str) is more standard
14894                    // Fall through to standard TRIM handling
14895                }
14896            }
14897        }
14898
14899        self.write_keyword("TRIM");
14900        self.write("(");
14901        // When BOTH is specified without trim characters, simplify to just TRIM(str)
14902        let use_standard = f.sql_standard_syntax
14903            && !(f.position_explicit && f.characters.is_none() && matches!(f.position, TrimPosition::Both));
14904        if use_standard {
14905            // SQL standard syntax: TRIM(BOTH chars FROM str)
14906            // Only output position if it was explicitly specified
14907            if f.position_explicit {
14908                match f.position {
14909                    TrimPosition::Both => self.write_keyword("BOTH"),
14910                    TrimPosition::Leading => self.write_keyword("LEADING"),
14911                    TrimPosition::Trailing => self.write_keyword("TRAILING"),
14912                }
14913                self.write_space();
14914            }
14915            if let Some(chars) = &f.characters {
14916                self.generate_expression(chars)?;
14917                self.write_space();
14918            }
14919            self.write_keyword("FROM");
14920            self.write_space();
14921            self.generate_expression(&f.this)?;
14922        } else {
14923            // Simple function syntax: TRIM(str) or TRIM(str, chars)
14924            self.generate_expression(&f.this)?;
14925            if let Some(chars) = &f.characters {
14926                self.write(", ");
14927                self.generate_expression(chars)?;
14928            }
14929        }
14930        self.write(")");
14931        Ok(())
14932    }
14933
14934    fn generate_replace(&mut self, f: &ReplaceFunc) -> Result<()> {
14935        self.write_keyword("REPLACE");
14936        self.write("(");
14937        self.generate_expression(&f.this)?;
14938        self.write(", ");
14939        self.generate_expression(&f.old)?;
14940        self.write(", ");
14941        self.generate_expression(&f.new)?;
14942        self.write(")");
14943        Ok(())
14944    }
14945
14946    fn generate_left_right(&mut self, name: &str, f: &LeftRightFunc) -> Result<()> {
14947        self.write_keyword(name);
14948        self.write("(");
14949        self.generate_expression(&f.this)?;
14950        self.write(", ");
14951        self.generate_expression(&f.length)?;
14952        self.write(")");
14953        Ok(())
14954    }
14955
14956    fn generate_repeat(&mut self, f: &RepeatFunc) -> Result<()> {
14957        self.write_keyword("REPEAT");
14958        self.write("(");
14959        self.generate_expression(&f.this)?;
14960        self.write(", ");
14961        self.generate_expression(&f.times)?;
14962        self.write(")");
14963        Ok(())
14964    }
14965
14966    fn generate_pad(&mut self, name: &str, f: &PadFunc) -> Result<()> {
14967        self.write_keyword(name);
14968        self.write("(");
14969        self.generate_expression(&f.this)?;
14970        self.write(", ");
14971        self.generate_expression(&f.length)?;
14972        if let Some(fill) = &f.fill {
14973            self.write(", ");
14974            self.generate_expression(fill)?;
14975        }
14976        self.write(")");
14977        Ok(())
14978    }
14979
14980    fn generate_split(&mut self, f: &SplitFunc) -> Result<()> {
14981        self.write_keyword("SPLIT");
14982        self.write("(");
14983        self.generate_expression(&f.this)?;
14984        self.write(", ");
14985        self.generate_expression(&f.delimiter)?;
14986        self.write(")");
14987        Ok(())
14988    }
14989
14990    fn generate_regexp_like(&mut self, f: &RegexpFunc) -> Result<()> {
14991        use crate::dialects::DialectType;
14992        // PostgreSQL uses ~ operator for regex matching
14993        if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) && f.flags.is_none() {
14994            self.generate_expression(&f.this)?;
14995            self.write(" ~ ");
14996            self.generate_expression(&f.pattern)?;
14997        } else if matches!(self.config.dialect, Some(DialectType::SingleStore) | Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)) && f.flags.is_none() {
14998            // SingleStore/Spark/Hive/Databricks use RLIKE infix operator
14999            self.generate_expression(&f.this)?;
15000            self.write_keyword(" RLIKE ");
15001            self.generate_expression(&f.pattern)?;
15002        } else if matches!(self.config.dialect, Some(DialectType::StarRocks)) {
15003            // StarRocks uses REGEXP function syntax
15004            self.write_keyword("REGEXP");
15005            self.write("(");
15006            self.generate_expression(&f.this)?;
15007            self.write(", ");
15008            self.generate_expression(&f.pattern)?;
15009            if let Some(flags) = &f.flags {
15010                self.write(", ");
15011                self.generate_expression(flags)?;
15012            }
15013            self.write(")");
15014        } else {
15015            self.write_keyword("REGEXP_LIKE");
15016            self.write("(");
15017            self.generate_expression(&f.this)?;
15018            self.write(", ");
15019            self.generate_expression(&f.pattern)?;
15020            if let Some(flags) = &f.flags {
15021                self.write(", ");
15022                self.generate_expression(flags)?;
15023            }
15024            self.write(")");
15025        }
15026        Ok(())
15027    }
15028
15029    fn generate_regexp_replace(&mut self, f: &RegexpReplaceFunc) -> Result<()> {
15030        self.write_keyword("REGEXP_REPLACE");
15031        self.write("(");
15032        self.generate_expression(&f.this)?;
15033        self.write(", ");
15034        self.generate_expression(&f.pattern)?;
15035        self.write(", ");
15036        self.generate_expression(&f.replacement)?;
15037        if let Some(flags) = &f.flags {
15038            self.write(", ");
15039            self.generate_expression(flags)?;
15040        }
15041        self.write(")");
15042        Ok(())
15043    }
15044
15045    fn generate_regexp_extract(&mut self, f: &RegexpExtractFunc) -> Result<()> {
15046        self.write_keyword("REGEXP_EXTRACT");
15047        self.write("(");
15048        self.generate_expression(&f.this)?;
15049        self.write(", ");
15050        self.generate_expression(&f.pattern)?;
15051        if let Some(group) = &f.group {
15052            self.write(", ");
15053            self.generate_expression(group)?;
15054        }
15055        self.write(")");
15056        Ok(())
15057    }
15058
15059    // Math function generators
15060
15061    fn generate_round(&mut self, f: &RoundFunc) -> Result<()> {
15062        self.write_keyword("ROUND");
15063        self.write("(");
15064        self.generate_expression(&f.this)?;
15065        if let Some(decimals) = &f.decimals {
15066            self.write(", ");
15067            self.generate_expression(decimals)?;
15068        }
15069        self.write(")");
15070        Ok(())
15071    }
15072
15073    fn generate_floor(&mut self, f: &FloorFunc) -> Result<()> {
15074        self.write_keyword("FLOOR");
15075        self.write("(");
15076        self.generate_expression(&f.this)?;
15077        // Handle Druid-style FLOOR(time TO unit) syntax
15078        if let Some(to) = &f.to {
15079            self.write(" ");
15080            self.write_keyword("TO");
15081            self.write(" ");
15082            self.generate_expression(to)?;
15083        } else if let Some(scale) = &f.scale {
15084            self.write(", ");
15085            self.generate_expression(scale)?;
15086        }
15087        self.write(")");
15088        Ok(())
15089    }
15090
15091    fn generate_ceil(&mut self, f: &CeilFunc) -> Result<()> {
15092        self.write_keyword("CEIL");
15093        self.write("(");
15094        self.generate_expression(&f.this)?;
15095        // Handle Druid-style CEIL(time TO unit) syntax
15096        if let Some(to) = &f.to {
15097            self.write(" ");
15098            self.write_keyword("TO");
15099            self.write(" ");
15100            self.generate_expression(to)?;
15101        } else if let Some(decimals) = &f.decimals {
15102            self.write(", ");
15103            self.generate_expression(decimals)?;
15104        }
15105        self.write(")");
15106        Ok(())
15107    }
15108
15109    fn generate_log(&mut self, f: &LogFunc) -> Result<()> {
15110        self.write_keyword("LOG");
15111        self.write("(");
15112        if let Some(base) = &f.base {
15113            self.generate_expression(base)?;
15114            self.write(", ");
15115        }
15116        self.generate_expression(&f.this)?;
15117        self.write(")");
15118        Ok(())
15119    }
15120
15121    // Date/time function generators
15122
15123    fn generate_current_time(&mut self, f: &CurrentTime) -> Result<()> {
15124        self.write_keyword("CURRENT_TIME");
15125        if let Some(precision) = f.precision {
15126            self.write(&format!("({})", precision));
15127        }
15128        Ok(())
15129    }
15130
15131    fn generate_current_timestamp(&mut self, f: &CurrentTimestamp) -> Result<()> {
15132        use crate::dialects::DialectType;
15133
15134        // Oracle/Redshift SYSDATE handling
15135        if f.sysdate {
15136            match self.config.dialect {
15137                Some(DialectType::Oracle) | Some(DialectType::Redshift) => {
15138                    self.write_keyword("SYSDATE");
15139                    return Ok(());
15140                }
15141                Some(DialectType::Snowflake) => {
15142                    // Snowflake uses SYSDATE() function
15143                    self.write_keyword("SYSDATE");
15144                    self.write("()");
15145                    return Ok(());
15146                }
15147                _ => {
15148                    // Other dialects use CURRENT_TIMESTAMP for SYSDATE
15149                }
15150            }
15151        }
15152
15153        self.write_keyword("CURRENT_TIMESTAMP");
15154        // MySQL, Spark, Hive always use CURRENT_TIMESTAMP() with parentheses
15155        if let Some(precision) = f.precision {
15156            self.write(&format!("({})", precision));
15157        } else if matches!(
15158            self.config.dialect,
15159            Some(crate::dialects::DialectType::MySQL) | Some(crate::dialects::DialectType::SingleStore) |
15160            Some(crate::dialects::DialectType::TiDB) | Some(crate::dialects::DialectType::Spark) |
15161            Some(crate::dialects::DialectType::Hive) |
15162            Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::ClickHouse) |
15163            Some(crate::dialects::DialectType::BigQuery) | Some(crate::dialects::DialectType::Snowflake)
15164        ) {
15165            self.write("()");
15166        }
15167        Ok(())
15168    }
15169
15170    fn generate_at_time_zone(&mut self, f: &AtTimeZone) -> Result<()> {
15171        // Exasol uses CONVERT_TZ(timestamp, 'UTC', zone) instead of AT TIME ZONE
15172        if self.config.dialect == Some(DialectType::Exasol) {
15173            self.write_keyword("CONVERT_TZ");
15174            self.write("(");
15175            self.generate_expression(&f.this)?;
15176            self.write(", 'UTC', ");
15177            self.generate_expression(&f.zone)?;
15178            self.write(")");
15179            return Ok(());
15180        }
15181
15182        self.generate_expression(&f.this)?;
15183        self.write_space();
15184        self.write_keyword("AT TIME ZONE");
15185        self.write_space();
15186        self.generate_expression(&f.zone)?;
15187        Ok(())
15188    }
15189
15190    fn generate_date_add(&mut self, f: &DateAddFunc, name: &str) -> Result<()> {
15191        use crate::dialects::DialectType;
15192
15193        // Presto/Trino use DATE_ADD('unit', interval, date) format
15194        // with the interval cast to BIGINT when needed
15195        let is_presto_like = matches!(
15196            self.config.dialect,
15197            Some(DialectType::Presto) | Some(DialectType::Trino)
15198        );
15199
15200        if is_presto_like {
15201            self.write_keyword(name);
15202            self.write("(");
15203            // Unit as string literal
15204            self.write("'");
15205            self.write_simple_interval_unit(&f.unit, false);
15206            self.write("'");
15207            self.write(", ");
15208            // Interval - wrap in CAST(...AS BIGINT) if it doesn't return integer type
15209            let needs_cast = !self.returns_integer_type(&f.interval);
15210            if needs_cast {
15211                self.write_keyword("CAST");
15212                self.write("(");
15213            }
15214            self.generate_expression(&f.interval)?;
15215            if needs_cast {
15216                self.write_space();
15217                self.write_keyword("AS");
15218                self.write_space();
15219                self.write_keyword("BIGINT");
15220                self.write(")");
15221            }
15222            self.write(", ");
15223            self.generate_expression(&f.this)?;
15224            self.write(")");
15225        } else {
15226            self.write_keyword(name);
15227            self.write("(");
15228            self.generate_expression(&f.this)?;
15229            self.write(", ");
15230            self.write_keyword("INTERVAL");
15231            self.write_space();
15232            self.generate_expression(&f.interval)?;
15233            self.write_space();
15234            self.write_simple_interval_unit(&f.unit, false); // Use singular form for DATEADD
15235            self.write(")");
15236        }
15237        Ok(())
15238    }
15239
15240    /// Check if an expression returns an integer type (doesn't need cast to BIGINT in Presto DATE_ADD)
15241    /// This is a heuristic to avoid full type inference
15242    fn returns_integer_type(&self, expr: &Expression) -> bool {
15243        use crate::expressions::{DataType, Literal};
15244        match expr {
15245            // Integer literals (no decimal point)
15246            Expression::Literal(Literal::Number(n)) => !n.contains('.'),
15247
15248            // FLOOR(x) returns integer if x is integer
15249            Expression::Floor(f) => self.returns_integer_type(&f.this),
15250
15251            // ROUND(x) returns integer if x is integer
15252            Expression::Round(f) => {
15253                // Only if no decimals arg or it's returning an integer
15254                f.decimals.is_none() && self.returns_integer_type(&f.this)
15255            }
15256
15257            // SIGN returns integer if input is integer
15258            Expression::Sign(f) => self.returns_integer_type(&f.this),
15259
15260            // ABS returns the same type as input
15261            Expression::Abs(f) => self.returns_integer_type(&f.this),
15262
15263            // Arithmetic operations on integers return integers
15264            Expression::Mul(op) => self.returns_integer_type(&op.left) && self.returns_integer_type(&op.right),
15265            Expression::Add(op) => self.returns_integer_type(&op.left) && self.returns_integer_type(&op.right),
15266            Expression::Sub(op) => self.returns_integer_type(&op.left) && self.returns_integer_type(&op.right),
15267            Expression::Mod(op) => self.returns_integer_type(&op.left),
15268
15269            // CAST(x AS BIGINT/INT/INTEGER/SMALLINT/TINYINT) returns integer
15270            Expression::Cast(c) => matches!(&c.to,
15271                DataType::BigInt { .. } | DataType::Int { .. } |
15272                DataType::SmallInt { .. } | DataType::TinyInt { .. }
15273            ),
15274
15275            // Negation: -x returns integer if x is integer
15276            Expression::Neg(op) => self.returns_integer_type(&op.this),
15277
15278            // Parenthesized expression
15279            Expression::Paren(p) => self.returns_integer_type(&p.this),
15280
15281            // Column references and most expressions are assumed to need casting
15282            // since we don't have full type information
15283            _ => false,
15284        }
15285    }
15286
15287    fn generate_datediff(&mut self, f: &DateDiffFunc) -> Result<()> {
15288        self.write_keyword("DATEDIFF");
15289        self.write("(");
15290        if let Some(unit) = &f.unit {
15291            self.write_simple_interval_unit(unit, false); // Use singular form for DATEDIFF
15292            self.write(", ");
15293        }
15294        self.generate_expression(&f.this)?;
15295        self.write(", ");
15296        self.generate_expression(&f.expression)?;
15297        self.write(")");
15298        Ok(())
15299    }
15300
15301    fn generate_date_trunc(&mut self, f: &DateTruncFunc) -> Result<()> {
15302        self.write_keyword("DATE_TRUNC");
15303        self.write("('");
15304        self.write_datetime_field(&f.unit);
15305        self.write("', ");
15306        self.generate_expression(&f.this)?;
15307        self.write(")");
15308        Ok(())
15309    }
15310
15311    fn generate_last_day(&mut self, f: &LastDayFunc) -> Result<()> {
15312        use crate::dialects::DialectType;
15313        use crate::expressions::DateTimeField;
15314
15315        self.write_keyword("LAST_DAY");
15316        self.write("(");
15317        self.generate_expression(&f.this)?;
15318        if let Some(unit) = &f.unit {
15319            self.write(", ");
15320            // BigQuery: strip week-start modifier from WEEK(SUNDAY), WEEK(MONDAY), etc.
15321            // WEEK(SUNDAY) -> WEEK
15322            if matches!(self.config.dialect, Some(DialectType::BigQuery)) {
15323                if let DateTimeField::WeekWithModifier(_) = unit {
15324                    self.write_keyword("WEEK");
15325                } else {
15326                    self.write_datetime_field(unit);
15327                }
15328            } else {
15329                self.write_datetime_field(unit);
15330            }
15331        }
15332        self.write(")");
15333        Ok(())
15334    }
15335
15336    fn generate_extract(&mut self, f: &ExtractFunc) -> Result<()> {
15337        // TSQL/Fabric use DATEPART(part, expr) instead of EXTRACT(part FROM expr)
15338        if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
15339            self.write_keyword("DATEPART");
15340            self.write("(");
15341            self.write_datetime_field(&f.field);
15342            self.write(", ");
15343            self.generate_expression(&f.this)?;
15344            self.write(")");
15345            return Ok(());
15346        }
15347        self.write_keyword("EXTRACT");
15348        self.write("(");
15349        // Hive/Spark use lowercase datetime fields in EXTRACT
15350        if matches!(self.config.dialect, Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)) {
15351            self.write_datetime_field_lower(&f.field);
15352        } else {
15353            self.write_datetime_field(&f.field);
15354        }
15355        self.write_space();
15356        self.write_keyword("FROM");
15357        self.write_space();
15358        self.generate_expression(&f.this)?;
15359        self.write(")");
15360        Ok(())
15361    }
15362
15363    fn generate_to_date(&mut self, f: &ToDateFunc) -> Result<()> {
15364        self.write_keyword("TO_DATE");
15365        self.write("(");
15366        self.generate_expression(&f.this)?;
15367        if let Some(format) = &f.format {
15368            self.write(", ");
15369            self.generate_expression(format)?;
15370        }
15371        self.write(")");
15372        Ok(())
15373    }
15374
15375    fn generate_to_timestamp(&mut self, f: &ToTimestampFunc) -> Result<()> {
15376        self.write_keyword("TO_TIMESTAMP");
15377        self.write("(");
15378        self.generate_expression(&f.this)?;
15379        if let Some(format) = &f.format {
15380            self.write(", ");
15381            self.generate_expression(format)?;
15382        }
15383        self.write(")");
15384        Ok(())
15385    }
15386
15387    // Control flow function generators
15388
15389    fn generate_if_func(&mut self, f: &IfFunc) -> Result<()> {
15390        use crate::dialects::DialectType;
15391
15392        // Exasol uses IF condition THEN true_value ELSE false_value ENDIF syntax
15393        if self.config.dialect == Some(DialectType::Exasol) {
15394            self.write_keyword("IF");
15395            self.write_space();
15396            self.generate_expression(&f.condition)?;
15397            self.write_space();
15398            self.write_keyword("THEN");
15399            self.write_space();
15400            self.generate_expression(&f.true_value)?;
15401            if let Some(false_val) = &f.false_value {
15402                self.write_space();
15403                self.write_keyword("ELSE");
15404                self.write_space();
15405                self.generate_expression(false_val)?;
15406            }
15407            self.write_space();
15408            self.write_keyword("ENDIF");
15409            return Ok(());
15410        }
15411
15412        // Choose function name based on target dialect
15413        let func_name = match self.config.dialect {
15414            Some(DialectType::Snowflake) => "IFF",
15415            Some(DialectType::SQLite) | Some(DialectType::TSQL) => "IIF",
15416            _ => "IF",
15417        };
15418        self.write_keyword(func_name);
15419        self.write("(");
15420        self.generate_expression(&f.condition)?;
15421        self.write(", ");
15422        self.generate_expression(&f.true_value)?;
15423        if let Some(false_val) = &f.false_value {
15424            self.write(", ");
15425            self.generate_expression(false_val)?;
15426        }
15427        self.write(")");
15428        Ok(())
15429    }
15430
15431    fn generate_nvl2(&mut self, f: &Nvl2Func) -> Result<()> {
15432        self.write_keyword("NVL2");
15433        self.write("(");
15434        self.generate_expression(&f.this)?;
15435        self.write(", ");
15436        self.generate_expression(&f.true_value)?;
15437        self.write(", ");
15438        self.generate_expression(&f.false_value)?;
15439        self.write(")");
15440        Ok(())
15441    }
15442
15443    // Typed aggregate function generators
15444
15445    fn generate_count(&mut self, f: &CountFunc) -> Result<()> {
15446        // Use normalize_functions for COUNT to respect ClickHouse case preservation
15447        let count_name = match self.config.normalize_functions {
15448            NormalizeFunctions::Upper => "COUNT".to_string(),
15449            NormalizeFunctions::Lower => "count".to_string(),
15450            NormalizeFunctions::None => f.original_name.clone().unwrap_or_else(|| "COUNT".to_string()),
15451        };
15452        self.write(&count_name);
15453        self.write("(");
15454        if f.distinct {
15455            self.write_keyword("DISTINCT");
15456            self.write_space();
15457        }
15458        if f.star {
15459            self.write("*");
15460        } else if let Some(ref expr) = f.this {
15461            // For COUNT(DISTINCT a, b), unwrap the Tuple to avoid extra parentheses
15462            if let Expression::Tuple(tuple) = expr {
15463                // Check if we need to transform multi-arg COUNT DISTINCT
15464                // When dialect doesn't support multi_arg_distinct, transform:
15465                // COUNT(DISTINCT a, b) -> COUNT(DISTINCT CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE (a, b) END)
15466                let needs_transform = f.distinct
15467                    && tuple.expressions.len() > 1
15468                    && !self.config.multi_arg_distinct;
15469
15470                if needs_transform {
15471                    // Generate: CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE (a, b) END
15472                    self.write_keyword("CASE");
15473                    for e in &tuple.expressions {
15474                        self.write_space();
15475                        self.write_keyword("WHEN");
15476                        self.write_space();
15477                        self.generate_expression(e)?;
15478                        self.write_space();
15479                        self.write_keyword("IS NULL THEN NULL");
15480                    }
15481                    self.write_space();
15482                    self.write_keyword("ELSE");
15483                    self.write(" (");
15484                    for (i, e) in tuple.expressions.iter().enumerate() {
15485                        if i > 0 {
15486                            self.write(", ");
15487                        }
15488                        self.generate_expression(e)?;
15489                    }
15490                    self.write(")");
15491                    self.write_space();
15492                    self.write_keyword("END");
15493                } else {
15494                    for (i, e) in tuple.expressions.iter().enumerate() {
15495                        if i > 0 {
15496                            self.write(", ");
15497                        }
15498                        self.generate_expression(e)?;
15499                    }
15500                }
15501            } else {
15502                self.generate_expression(expr)?;
15503            }
15504        }
15505        // RESPECT NULLS / IGNORE NULLS
15506        if let Some(ignore) = f.ignore_nulls {
15507            self.write_space();
15508            if ignore {
15509                self.write_keyword("IGNORE NULLS");
15510            } else {
15511                self.write_keyword("RESPECT NULLS");
15512            }
15513        }
15514        self.write(")");
15515        if let Some(ref filter) = f.filter {
15516            self.write_space();
15517            self.write_keyword("FILTER");
15518            self.write("(");
15519            self.write_keyword("WHERE");
15520            self.write_space();
15521            self.generate_expression(filter)?;
15522            self.write(")");
15523        }
15524        Ok(())
15525    }
15526
15527    fn generate_agg_func(&mut self, name: &str, f: &AggFunc) -> Result<()> {
15528        // Apply function name normalization based on config
15529        let func_name = match self.config.normalize_functions {
15530            NormalizeFunctions::Upper => name.to_uppercase(),
15531            NormalizeFunctions::Lower => name.to_lowercase(),
15532            NormalizeFunctions::None => {
15533                // Use the original function name from parsing if available,
15534                // otherwise fall back to lowercase of the hardcoded constant
15535                if let Some(ref original) = f.name {
15536                    original.clone()
15537                } else {
15538                    name.to_lowercase()
15539                }
15540            }
15541        };
15542        self.write(&func_name);
15543        self.write("(");
15544        if f.distinct {
15545            self.write_keyword("DISTINCT");
15546            self.write_space();
15547        }
15548        // Skip generating the expression if it's a NULL placeholder for zero-arg aggregates like MODE()
15549        if !matches!(f.this, Expression::Null(_)) {
15550            self.generate_expression(&f.this)?;
15551        }
15552        // Generate IGNORE NULLS / RESPECT NULLS inside parens if config says so (BigQuery style)
15553        // DuckDB doesn't support IGNORE NULLS / RESPECT NULLS in aggregate functions - skip it
15554        if self.config.ignore_nulls_in_func && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
15555            match f.ignore_nulls {
15556                Some(true) => {
15557                    self.write_space();
15558                    self.write_keyword("IGNORE NULLS");
15559                }
15560                Some(false) => {
15561                    self.write_space();
15562                    self.write_keyword("RESPECT NULLS");
15563                }
15564                None => {}
15565            }
15566        }
15567        // Generate HAVING MAX/MIN if present (BigQuery syntax)
15568        // e.g., ANY_VALUE(fruit HAVING MAX sold)
15569        if let Some((ref expr, is_max)) = f.having_max {
15570            self.write_space();
15571            self.write_keyword("HAVING");
15572            self.write_space();
15573            if is_max {
15574                self.write_keyword("MAX");
15575            } else {
15576                self.write_keyword("MIN");
15577            }
15578            self.write_space();
15579            self.generate_expression(expr)?;
15580        }
15581        // Generate ORDER BY if present (for aggregates like ARRAY_AGG(x ORDER BY y))
15582        if !f.order_by.is_empty() {
15583            self.write_space();
15584            self.write_keyword("ORDER BY");
15585            self.write_space();
15586            for (i, ord) in f.order_by.iter().enumerate() {
15587                if i > 0 { self.write(", "); }
15588                self.generate_ordered(ord)?;
15589            }
15590        }
15591        // Generate LIMIT if present (for aggregates like ARRAY_AGG(x ORDER BY y LIMIT 2))
15592        if let Some(ref limit) = f.limit {
15593            self.write_space();
15594            self.write_keyword("LIMIT");
15595            self.write_space();
15596            // Check if this is a Tuple representing LIMIT offset, count
15597            if let Expression::Tuple(t) = limit.as_ref() {
15598                if t.expressions.len() == 2 {
15599                    self.generate_expression(&t.expressions[0])?;
15600                    self.write(", ");
15601                    self.generate_expression(&t.expressions[1])?;
15602                } else {
15603                    self.generate_expression(limit)?;
15604                }
15605            } else {
15606                self.generate_expression(limit)?;
15607            }
15608        }
15609        self.write(")");
15610        // Generate IGNORE NULLS / RESPECT NULLS outside parens if config says so (standard style)
15611        // DuckDB doesn't support IGNORE NULLS / RESPECT NULLS in aggregate functions - skip it
15612        if !self.config.ignore_nulls_in_func && !matches!(self.config.dialect, Some(DialectType::DuckDB)) {
15613            match f.ignore_nulls {
15614                Some(true) => {
15615                    self.write_space();
15616                    self.write_keyword("IGNORE NULLS");
15617                }
15618                Some(false) => {
15619                    self.write_space();
15620                    self.write_keyword("RESPECT NULLS");
15621                }
15622                None => {}
15623            }
15624        }
15625        if let Some(ref filter) = f.filter {
15626            self.write_space();
15627            self.write_keyword("FILTER");
15628            self.write("(");
15629            self.write_keyword("WHERE");
15630            self.write_space();
15631            self.generate_expression(filter)?;
15632            self.write(")");
15633        }
15634        Ok(())
15635    }
15636
15637    fn generate_group_concat(&mut self, f: &GroupConcatFunc) -> Result<()> {
15638        self.write_keyword("GROUP_CONCAT");
15639        self.write("(");
15640        if f.distinct {
15641            self.write_keyword("DISTINCT");
15642            self.write_space();
15643        }
15644        self.generate_expression(&f.this)?;
15645        if let Some(ref order_by) = f.order_by {
15646            self.write_space();
15647            self.write_keyword("ORDER BY");
15648            self.write_space();
15649            for (i, ord) in order_by.iter().enumerate() {
15650                if i > 0 { self.write(", "); }
15651                self.generate_ordered(ord)?;
15652            }
15653        }
15654        if let Some(ref sep) = f.separator {
15655            // SQLite uses GROUP_CONCAT(x, sep) syntax (comma-separated)
15656            // MySQL and others use GROUP_CONCAT(x SEPARATOR sep) syntax
15657            if matches!(self.config.dialect, Some(crate::dialects::DialectType::SQLite)) {
15658                self.write(", ");
15659                self.generate_expression(sep)?;
15660            } else {
15661                self.write_space();
15662                self.write_keyword("SEPARATOR");
15663                self.write_space();
15664                self.generate_expression(sep)?;
15665            }
15666        }
15667        self.write(")");
15668        if let Some(ref filter) = f.filter {
15669            self.write_space();
15670            self.write_keyword("FILTER");
15671            self.write("(");
15672            self.write_keyword("WHERE");
15673            self.write_space();
15674            self.generate_expression(filter)?;
15675            self.write(")");
15676        }
15677        Ok(())
15678    }
15679
15680    fn generate_string_agg(&mut self, f: &StringAggFunc) -> Result<()> {
15681        let is_tsql = matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL));
15682        self.write_keyword("STRING_AGG");
15683        self.write("(");
15684        if f.distinct {
15685            self.write_keyword("DISTINCT");
15686            self.write_space();
15687        }
15688        self.generate_expression(&f.this)?;
15689        if let Some(ref separator) = f.separator {
15690            self.write(", ");
15691            self.generate_expression(separator)?;
15692        }
15693        // For TSQL, ORDER BY goes in WITHIN GROUP clause after the closing paren
15694        if !is_tsql {
15695            if let Some(ref order_by) = f.order_by {
15696                self.write_space();
15697                self.write_keyword("ORDER BY");
15698                self.write_space();
15699                for (i, ord) in order_by.iter().enumerate() {
15700                    if i > 0 { self.write(", "); }
15701                    self.generate_ordered(ord)?;
15702                }
15703            }
15704        }
15705        if let Some(ref limit) = f.limit {
15706            self.write_space();
15707            self.write_keyword("LIMIT");
15708            self.write_space();
15709            self.generate_expression(limit)?;
15710        }
15711        self.write(")");
15712        // TSQL uses WITHIN GROUP (ORDER BY ...) after the function call
15713        if is_tsql {
15714            if let Some(ref order_by) = f.order_by {
15715                self.write_space();
15716                self.write_keyword("WITHIN GROUP");
15717                self.write(" (");
15718                self.write_keyword("ORDER BY");
15719                self.write_space();
15720                for (i, ord) in order_by.iter().enumerate() {
15721                    if i > 0 { self.write(", "); }
15722                    self.generate_ordered(ord)?;
15723                }
15724                self.write(")");
15725            }
15726        }
15727        if let Some(ref filter) = f.filter {
15728            self.write_space();
15729            self.write_keyword("FILTER");
15730            self.write("(");
15731            self.write_keyword("WHERE");
15732            self.write_space();
15733            self.generate_expression(filter)?;
15734            self.write(")");
15735        }
15736        Ok(())
15737    }
15738
15739    fn generate_listagg(&mut self, f: &ListAggFunc) -> Result<()> {
15740        use crate::dialects::DialectType;
15741        self.write_keyword("LISTAGG");
15742        self.write("(");
15743        if f.distinct {
15744            self.write_keyword("DISTINCT");
15745            self.write_space();
15746        }
15747        self.generate_expression(&f.this)?;
15748        if let Some(ref sep) = f.separator {
15749            self.write(", ");
15750            self.generate_expression(sep)?;
15751        } else if matches!(self.config.dialect, Some(DialectType::Trino) | Some(DialectType::Presto)) {
15752            // Trino/Presto require explicit separator; default to ','
15753            self.write(", ','");
15754        }
15755        if let Some(ref overflow) = f.on_overflow {
15756            self.write_space();
15757            self.write_keyword("ON OVERFLOW");
15758            self.write_space();
15759            match overflow {
15760                ListAggOverflow::Error => self.write_keyword("ERROR"),
15761                ListAggOverflow::Truncate { filler, with_count } => {
15762                    self.write_keyword("TRUNCATE");
15763                    if let Some(ref fill) = filler {
15764                        self.write_space();
15765                        self.generate_expression(fill)?;
15766                    }
15767                    if *with_count {
15768                        self.write_space();
15769                        self.write_keyword("WITH COUNT");
15770                    } else {
15771                        self.write_space();
15772                        self.write_keyword("WITHOUT COUNT");
15773                    }
15774                }
15775            }
15776        }
15777        self.write(")");
15778        if let Some(ref order_by) = f.order_by {
15779            self.write_space();
15780            self.write_keyword("WITHIN GROUP");
15781            self.write(" (");
15782            self.write_keyword("ORDER BY");
15783            self.write_space();
15784            for (i, ord) in order_by.iter().enumerate() {
15785                if i > 0 { self.write(", "); }
15786                self.generate_ordered(ord)?;
15787            }
15788            self.write(")");
15789        }
15790        if let Some(ref filter) = f.filter {
15791            self.write_space();
15792            self.write_keyword("FILTER");
15793            self.write("(");
15794            self.write_keyword("WHERE");
15795            self.write_space();
15796            self.generate_expression(filter)?;
15797            self.write(")");
15798        }
15799        Ok(())
15800    }
15801
15802    fn generate_sum_if(&mut self, f: &SumIfFunc) -> Result<()> {
15803        self.write_keyword("SUM_IF");
15804        self.write("(");
15805        self.generate_expression(&f.this)?;
15806        self.write(", ");
15807        self.generate_expression(&f.condition)?;
15808        self.write(")");
15809        if let Some(ref filter) = f.filter {
15810            self.write_space();
15811            self.write_keyword("FILTER");
15812            self.write("(");
15813            self.write_keyword("WHERE");
15814            self.write_space();
15815            self.generate_expression(filter)?;
15816            self.write(")");
15817        }
15818        Ok(())
15819    }
15820
15821    fn generate_approx_percentile(&mut self, f: &ApproxPercentileFunc) -> Result<()> {
15822        self.write_keyword("APPROX_PERCENTILE");
15823        self.write("(");
15824        self.generate_expression(&f.this)?;
15825        self.write(", ");
15826        self.generate_expression(&f.percentile)?;
15827        if let Some(ref acc) = f.accuracy {
15828            self.write(", ");
15829            self.generate_expression(acc)?;
15830        }
15831        self.write(")");
15832        if let Some(ref filter) = f.filter {
15833            self.write_space();
15834            self.write_keyword("FILTER");
15835            self.write("(");
15836            self.write_keyword("WHERE");
15837            self.write_space();
15838            self.generate_expression(filter)?;
15839            self.write(")");
15840        }
15841        Ok(())
15842    }
15843
15844    fn generate_percentile(&mut self, name: &str, f: &PercentileFunc) -> Result<()> {
15845        self.write_keyword(name);
15846        self.write("(");
15847        self.generate_expression(&f.percentile)?;
15848        self.write(")");
15849        if let Some(ref order_by) = f.order_by {
15850            self.write_space();
15851            self.write_keyword("WITHIN GROUP");
15852            self.write(" (");
15853            self.write_keyword("ORDER BY");
15854            self.write_space();
15855            self.generate_expression(&f.this)?;
15856            for ord in order_by.iter() {
15857                if ord.desc {
15858                    self.write_space();
15859                    self.write_keyword("DESC");
15860                }
15861            }
15862            self.write(")");
15863        }
15864        if let Some(ref filter) = f.filter {
15865            self.write_space();
15866            self.write_keyword("FILTER");
15867            self.write("(");
15868            self.write_keyword("WHERE");
15869            self.write_space();
15870            self.generate_expression(filter)?;
15871            self.write(")");
15872        }
15873        Ok(())
15874    }
15875
15876    // Window function generators
15877
15878    fn generate_ntile(&mut self, f: &NTileFunc) -> Result<()> {
15879        self.write_keyword("NTILE");
15880        self.write("(");
15881        if let Some(num_buckets) = &f.num_buckets {
15882            self.generate_expression(num_buckets)?;
15883        }
15884        if let Some(order_by) = &f.order_by {
15885            self.write_keyword(" ORDER BY ");
15886            for (i, ob) in order_by.iter().enumerate() {
15887                if i > 0 { self.write(", "); }
15888                self.generate_ordered(ob)?;
15889            }
15890        }
15891        self.write(")");
15892        Ok(())
15893    }
15894
15895    fn generate_lead_lag(&mut self, name: &str, f: &LeadLagFunc) -> Result<()> {
15896        self.write_keyword(name);
15897        self.write("(");
15898        self.generate_expression(&f.this)?;
15899        if let Some(ref offset) = f.offset {
15900            self.write(", ");
15901            self.generate_expression(offset)?;
15902            if let Some(ref default) = f.default {
15903                self.write(", ");
15904                self.generate_expression(default)?;
15905            }
15906        }
15907        // IGNORE NULLS inside parens for dialects like BigQuery
15908        if f.ignore_nulls && self.config.ignore_nulls_in_func {
15909            self.write_space();
15910            self.write_keyword("IGNORE NULLS");
15911        }
15912        self.write(")");
15913        // IGNORE NULLS outside parens for other dialects
15914        if f.ignore_nulls && !self.config.ignore_nulls_in_func {
15915            self.write_space();
15916            self.write_keyword("IGNORE NULLS");
15917        }
15918        Ok(())
15919    }
15920
15921    fn generate_value_func(&mut self, name: &str, f: &ValueFunc) -> Result<()> {
15922        self.write_keyword(name);
15923        self.write("(");
15924        self.generate_expression(&f.this)?;
15925        // IGNORE NULLS / RESPECT NULLS inside parens for dialects like BigQuery
15926        if self.config.ignore_nulls_in_func {
15927            match f.ignore_nulls {
15928                Some(true) => {
15929                    self.write_space();
15930                    self.write_keyword("IGNORE NULLS");
15931                }
15932                Some(false) => {
15933                    self.write_space();
15934                    self.write_keyword("RESPECT NULLS");
15935                }
15936                None => {}
15937            }
15938        }
15939        self.write(")");
15940        // IGNORE NULLS / RESPECT NULLS outside parens for other dialects
15941        if !self.config.ignore_nulls_in_func {
15942            match f.ignore_nulls {
15943                Some(true) => {
15944                    self.write_space();
15945                    self.write_keyword("IGNORE NULLS");
15946                }
15947                Some(false) => {
15948                    self.write_space();
15949                    self.write_keyword("RESPECT NULLS");
15950                }
15951                None => {}
15952            }
15953        }
15954        Ok(())
15955    }
15956
15957    fn generate_nth_value(&mut self, f: &NthValueFunc) -> Result<()> {
15958        self.write_keyword("NTH_VALUE");
15959        self.write("(");
15960        self.generate_expression(&f.this)?;
15961        self.write(", ");
15962        self.generate_expression(&f.offset)?;
15963        // IGNORE NULLS / RESPECT NULLS inside parens for dialects like BigQuery
15964        if self.config.ignore_nulls_in_func {
15965            match f.ignore_nulls {
15966                Some(true) => {
15967                    self.write_space();
15968                    self.write_keyword("IGNORE NULLS");
15969                }
15970                Some(false) => {
15971                    self.write_space();
15972                    self.write_keyword("RESPECT NULLS");
15973                }
15974                None => {}
15975            }
15976        }
15977        self.write(")");
15978        // IGNORE NULLS / RESPECT NULLS outside parens for other dialects
15979        if !self.config.ignore_nulls_in_func {
15980            match f.ignore_nulls {
15981                Some(true) => {
15982                    self.write_space();
15983                    self.write_keyword("IGNORE NULLS");
15984                }
15985                Some(false) => {
15986                    self.write_space();
15987                    self.write_keyword("RESPECT NULLS");
15988                }
15989                None => {}
15990            }
15991        }
15992        Ok(())
15993    }
15994
15995    // Additional string function generators
15996
15997    fn generate_position(&mut self, f: &PositionFunc) -> Result<()> {
15998        // Standard syntax: POSITION(substr IN str)
15999        // ClickHouse prefers comma syntax with reversed arg order: POSITION(str, substr[, start])
16000        if matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse)) {
16001            self.write_keyword("POSITION");
16002            self.write("(");
16003            self.generate_expression(&f.string)?;
16004            self.write(", ");
16005            self.generate_expression(&f.substring)?;
16006            if let Some(ref start) = f.start {
16007                self.write(", ");
16008                self.generate_expression(start)?;
16009            }
16010            self.write(")");
16011            return Ok(());
16012        }
16013
16014        self.write_keyword("POSITION");
16015        self.write("(");
16016        self.generate_expression(&f.substring)?;
16017        self.write_space();
16018        self.write_keyword("IN");
16019        self.write_space();
16020        self.generate_expression(&f.string)?;
16021        if let Some(ref start) = f.start {
16022            self.write(", ");
16023            self.generate_expression(start)?;
16024        }
16025        self.write(")");
16026        Ok(())
16027    }
16028
16029    // Additional math function generators
16030
16031    fn generate_rand(&mut self, f: &Rand) -> Result<()> {
16032        // Teradata RANDOM(lower, upper)
16033        if f.lower.is_some() || f.upper.is_some() {
16034            self.write_keyword("RANDOM");
16035            self.write("(");
16036            if let Some(ref lower) = f.lower {
16037                self.generate_expression(lower)?;
16038            }
16039            if let Some(ref upper) = f.upper {
16040                self.write(", ");
16041                self.generate_expression(upper)?;
16042            }
16043            self.write(")");
16044            return Ok(());
16045        }
16046        // Snowflake uses RANDOM instead of RAND, DuckDB uses RANDOM without seed
16047        let func_name = match self.config.dialect {
16048            Some(crate::dialects::DialectType::Snowflake) | Some(crate::dialects::DialectType::DuckDB) => "RANDOM",
16049            _ => "RAND",
16050        };
16051        self.write_keyword(func_name);
16052        self.write("(");
16053        // DuckDB doesn't support seeded RANDOM, so skip the seed
16054        if !matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB)) {
16055            if let Some(ref seed) = f.seed {
16056                self.generate_expression(seed)?;
16057            }
16058        }
16059        self.write(")");
16060        Ok(())
16061    }
16062
16063    fn generate_truncate_func(&mut self, f: &TruncateFunc) -> Result<()> {
16064        self.write_keyword("TRUNCATE");
16065        self.write("(");
16066        self.generate_expression(&f.this)?;
16067        if let Some(ref decimals) = f.decimals {
16068            self.write(", ");
16069            self.generate_expression(decimals)?;
16070        }
16071        self.write(")");
16072        Ok(())
16073    }
16074
16075    // Control flow generators
16076
16077    fn generate_decode(&mut self, f: &DecodeFunc) -> Result<()> {
16078        self.write_keyword("DECODE");
16079        self.write("(");
16080        self.generate_expression(&f.this)?;
16081        for (search, result) in &f.search_results {
16082            self.write(", ");
16083            self.generate_expression(search)?;
16084            self.write(", ");
16085            self.generate_expression(result)?;
16086        }
16087        if let Some(ref default) = f.default {
16088            self.write(", ");
16089            self.generate_expression(default)?;
16090        }
16091        self.write(")");
16092        Ok(())
16093    }
16094
16095    // Date/time function generators
16096
16097    fn generate_date_format(&mut self, name: &str, f: &DateFormatFunc) -> Result<()> {
16098        self.write_keyword(name);
16099        self.write("(");
16100        self.generate_expression(&f.this)?;
16101        self.write(", ");
16102        self.generate_expression(&f.format)?;
16103        self.write(")");
16104        Ok(())
16105    }
16106
16107    fn generate_from_unixtime(&mut self, f: &FromUnixtimeFunc) -> Result<()> {
16108        self.write_keyword("FROM_UNIXTIME");
16109        self.write("(");
16110        self.generate_expression(&f.this)?;
16111        if let Some(ref format) = f.format {
16112            self.write(", ");
16113            self.generate_expression(format)?;
16114        }
16115        self.write(")");
16116        Ok(())
16117    }
16118
16119    fn generate_unix_timestamp(&mut self, f: &UnixTimestampFunc) -> Result<()> {
16120        self.write_keyword("UNIX_TIMESTAMP");
16121        self.write("(");
16122        if let Some(ref expr) = f.this {
16123            self.generate_expression(expr)?;
16124            if let Some(ref format) = f.format {
16125                self.write(", ");
16126                self.generate_expression(format)?;
16127            }
16128        } else if matches!(
16129            self.config.dialect,
16130            Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)
16131        ) {
16132            // Spark/Hive: UNIX_TIMESTAMP() -> UNIX_TIMESTAMP(CURRENT_TIMESTAMP())
16133            self.write_keyword("CURRENT_TIMESTAMP");
16134            self.write("()");
16135        }
16136        self.write(")");
16137        Ok(())
16138    }
16139
16140    fn generate_make_date(&mut self, f: &MakeDateFunc) -> Result<()> {
16141        self.write_keyword("MAKE_DATE");
16142        self.write("(");
16143        self.generate_expression(&f.year)?;
16144        self.write(", ");
16145        self.generate_expression(&f.month)?;
16146        self.write(", ");
16147        self.generate_expression(&f.day)?;
16148        self.write(")");
16149        Ok(())
16150    }
16151
16152    fn generate_make_timestamp(&mut self, f: &MakeTimestampFunc) -> Result<()> {
16153        self.write_keyword("MAKE_TIMESTAMP");
16154        self.write("(");
16155        self.generate_expression(&f.year)?;
16156        self.write(", ");
16157        self.generate_expression(&f.month)?;
16158        self.write(", ");
16159        self.generate_expression(&f.day)?;
16160        self.write(", ");
16161        self.generate_expression(&f.hour)?;
16162        self.write(", ");
16163        self.generate_expression(&f.minute)?;
16164        self.write(", ");
16165        self.generate_expression(&f.second)?;
16166        if let Some(ref tz) = f.timezone {
16167            self.write(", ");
16168            self.generate_expression(tz)?;
16169        }
16170        self.write(")");
16171        Ok(())
16172    }
16173
16174    /// Extract field names from a struct expression (either Struct or Function named STRUCT with Alias args)
16175    fn extract_struct_field_names(expr: &Expression) -> Option<Vec<String>> {
16176        match expr {
16177            Expression::Struct(s) => {
16178                if s.fields.iter().all(|(name, _)| name.is_some()) {
16179                    Some(s.fields.iter().map(|(name, _)| name.as_deref().unwrap_or("").to_string()).collect())
16180                } else {
16181                    None
16182                }
16183            }
16184            Expression::Function(f) if f.name.to_uppercase() == "STRUCT" => {
16185                // Check if all args are Alias (named fields)
16186                if f.args.iter().all(|a| matches!(a, Expression::Alias(_))) {
16187                    Some(f.args.iter().filter_map(|a| {
16188                        if let Expression::Alias(alias) = a {
16189                            Some(alias.alias.name.clone())
16190                        } else {
16191                            None
16192                        }
16193                    }).collect())
16194                } else {
16195                    None
16196                }
16197            }
16198            _ => None,
16199        }
16200    }
16201
16202    /// Check if a struct expression has any unnamed fields
16203    fn struct_has_unnamed_fields(expr: &Expression) -> bool {
16204        match expr {
16205            Expression::Struct(s) => s.fields.iter().any(|(name, _)| name.is_none()),
16206            Expression::Function(f) if f.name.to_uppercase() == "STRUCT" => {
16207                f.args.iter().any(|a| !matches!(a, Expression::Alias(_)))
16208            }
16209            _ => false,
16210        }
16211    }
16212
16213    /// Get the field count of a struct expression
16214    fn struct_field_count(expr: &Expression) -> usize {
16215        match expr {
16216            Expression::Struct(s) => s.fields.len(),
16217            Expression::Function(f) if f.name.to_uppercase() == "STRUCT" => f.args.len(),
16218            _ => 0,
16219        }
16220    }
16221
16222    /// Apply field names to an unnamed struct expression, producing a new expression with names
16223    fn apply_struct_field_names(expr: &Expression, field_names: &[String]) -> Expression {
16224        match expr {
16225            Expression::Struct(s) => {
16226                let mut new_fields = Vec::with_capacity(s.fields.len());
16227                for (i, (name, value)) in s.fields.iter().enumerate() {
16228                    if name.is_none() && i < field_names.len() {
16229                        new_fields.push((Some(field_names[i].clone()), value.clone()));
16230                    } else {
16231                        new_fields.push((name.clone(), value.clone()));
16232                    }
16233                }
16234                Expression::Struct(Box::new(crate::expressions::Struct { fields: new_fields }))
16235            }
16236            Expression::Function(f) if f.name.to_uppercase() == "STRUCT" => {
16237                let mut new_args = Vec::with_capacity(f.args.len());
16238                for (i, arg) in f.args.iter().enumerate() {
16239                    if !matches!(arg, Expression::Alias(_)) && i < field_names.len() {
16240                        // Wrap the value in an Alias with the inherited name
16241                        new_args.push(Expression::Alias(Box::new(crate::expressions::Alias {
16242                            this: arg.clone(),
16243                            alias: crate::expressions::Identifier::new(field_names[i].clone()),
16244                            column_aliases: Vec::new(),
16245                            pre_alias_comments: Vec::new(),
16246                            trailing_comments: Vec::new(),
16247                        })));
16248                    } else {
16249                        new_args.push(arg.clone());
16250                    }
16251                }
16252                Expression::Function(Box::new(crate::expressions::Function {
16253                    name: f.name.clone(),
16254                    args: new_args,
16255                    distinct: f.distinct,
16256                    trailing_comments: f.trailing_comments.clone(),
16257                    use_bracket_syntax: f.use_bracket_syntax,
16258                    no_parens: f.no_parens,
16259                    quoted: f.quoted,
16260                }))
16261            }
16262            _ => expr.clone(),
16263        }
16264    }
16265
16266    /// Propagate struct field names from the first struct in an array to subsequent unnamed structs.
16267    /// This implements BigQuery's implicit field name inheritance for struct arrays.
16268    /// Handles both Expression::Struct and Expression::Function named "STRUCT".
16269    fn inherit_struct_field_names(expressions: &[Expression]) -> Vec<Expression> {
16270        let first = match expressions.first() {
16271            Some(e) => e,
16272            None => return expressions.to_vec(),
16273        };
16274
16275        let field_names = match Self::extract_struct_field_names(first) {
16276            Some(names) if !names.is_empty() => names,
16277            _ => return expressions.to_vec(),
16278        };
16279
16280        let mut result = Vec::with_capacity(expressions.len());
16281        for (idx, expr) in expressions.iter().enumerate() {
16282            if idx == 0 {
16283                result.push(expr.clone());
16284                continue;
16285            }
16286            // Check if this is a struct with unnamed fields that needs name propagation
16287            if Self::struct_field_count(expr) == field_names.len() && Self::struct_has_unnamed_fields(expr) {
16288                result.push(Self::apply_struct_field_names(expr, &field_names));
16289            } else {
16290                result.push(expr.clone());
16291            }
16292        }
16293        result
16294    }
16295
16296    // Array function generators
16297
16298    fn generate_array_constructor(&mut self, f: &ArrayConstructor) -> Result<()> {
16299        // Apply struct name inheritance for target dialects that need it
16300        // (DuckDB, Spark, Databricks, Hive, Snowflake, Presto, Trino)
16301        let needs_inheritance = matches!(self.config.dialect,
16302            Some(DialectType::DuckDB) | Some(DialectType::Spark)
16303            | Some(DialectType::Databricks) | Some(DialectType::Hive)
16304            | Some(DialectType::Snowflake) | Some(DialectType::Presto) | Some(DialectType::Trino)
16305        );
16306        let propagated: Vec<Expression>;
16307        let expressions = if needs_inheritance && f.expressions.len() > 1 {
16308            propagated = Self::inherit_struct_field_names(&f.expressions);
16309            &propagated
16310        } else {
16311            &f.expressions
16312        };
16313
16314        // Check if elements should be split onto multiple lines (pretty + too wide)
16315        let should_split = if self.config.pretty && !expressions.is_empty() {
16316            let mut expr_strings: Vec<String> = Vec::with_capacity(expressions.len());
16317            for expr in expressions {
16318                let mut temp_gen = Generator::with_config(self.config.clone());
16319                temp_gen.config.pretty = false;
16320                temp_gen.generate_expression(expr)?;
16321                expr_strings.push(temp_gen.output);
16322            }
16323            self.too_wide(&expr_strings)
16324        } else {
16325            false
16326        };
16327
16328        if f.bracket_notation {
16329            // For Spark/Databricks, use ARRAY(...) with parens
16330            // For Presto/Trino/PostgreSQL, use ARRAY[...] with keyword prefix
16331            // For others (DuckDB, Snowflake), use bare [...]
16332            let (open, close) = match self.config.dialect {
16333                Some(DialectType::Spark) | Some(DialectType::Databricks)
16334                | Some(DialectType::Hive) => {
16335                    self.write_keyword("ARRAY");
16336                    ("(", ")")
16337                }
16338                Some(DialectType::Presto) | Some(DialectType::Trino)
16339                | Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)
16340                | Some(DialectType::Materialize) | Some(DialectType::RisingWave)
16341                | Some(DialectType::CockroachDB) => {
16342                    self.write_keyword("ARRAY");
16343                    ("[", "]")
16344                }
16345                _ => ("[", "]"),
16346            };
16347            self.write(open);
16348            if should_split {
16349                self.write_newline();
16350                self.indent_level += 1;
16351                for (i, expr) in expressions.iter().enumerate() {
16352                    self.write_indent();
16353                    self.generate_expression(expr)?;
16354                    if i + 1 < expressions.len() {
16355                        self.write(",");
16356                    }
16357                    self.write_newline();
16358                }
16359                self.indent_level -= 1;
16360                self.write_indent();
16361            } else {
16362                for (i, expr) in expressions.iter().enumerate() {
16363                    if i > 0 { self.write(", "); }
16364                    self.generate_expression(expr)?;
16365                }
16366            }
16367            self.write(close);
16368        } else {
16369            // Use LIST keyword if that was the original syntax (DuckDB)
16370            if f.use_list_keyword {
16371                self.write_keyword("LIST");
16372            } else {
16373                self.write_keyword("ARRAY");
16374            }
16375            // For Spark/Hive, always use ARRAY(...) with parens
16376            let (open, close) = if matches!(self.config.dialect,
16377                Some(DialectType::Spark)
16378                | Some(DialectType::Databricks) | Some(DialectType::Hive)) {
16379                ("(", ")")
16380            } else {
16381                ("[", "]")
16382            };
16383            self.write(open);
16384            if should_split {
16385                self.write_newline();
16386                self.indent_level += 1;
16387                for (i, expr) in expressions.iter().enumerate() {
16388                    self.write_indent();
16389                    self.generate_expression(expr)?;
16390                    if i + 1 < expressions.len() {
16391                        self.write(",");
16392                    }
16393                    self.write_newline();
16394                }
16395                self.indent_level -= 1;
16396                self.write_indent();
16397            } else {
16398                for (i, expr) in expressions.iter().enumerate() {
16399                    if i > 0 { self.write(", "); }
16400                    self.generate_expression(expr)?;
16401                }
16402            }
16403            self.write(close);
16404        }
16405        Ok(())
16406    }
16407
16408    fn generate_array_sort(&mut self, f: &ArraySortFunc) -> Result<()> {
16409        self.write_keyword("ARRAY_SORT");
16410        self.write("(");
16411        self.generate_expression(&f.this)?;
16412        if let Some(ref comp) = f.comparator {
16413            self.write(", ");
16414            self.generate_expression(comp)?;
16415        }
16416        self.write(")");
16417        Ok(())
16418    }
16419
16420    fn generate_array_join(&mut self, name: &str, f: &ArrayJoinFunc) -> Result<()> {
16421        self.write_keyword(name);
16422        self.write("(");
16423        self.generate_expression(&f.this)?;
16424        self.write(", ");
16425        self.generate_expression(&f.separator)?;
16426        if let Some(ref null_rep) = f.null_replacement {
16427            self.write(", ");
16428            self.generate_expression(null_rep)?;
16429        }
16430        self.write(")");
16431        Ok(())
16432    }
16433
16434    fn generate_unnest(&mut self, f: &UnnestFunc) -> Result<()> {
16435        self.write_keyword("UNNEST");
16436        self.write("(");
16437        self.generate_expression(&f.this)?;
16438        for extra in &f.expressions {
16439            self.write(", ");
16440            self.generate_expression(extra)?;
16441        }
16442        self.write(")");
16443        if f.with_ordinality {
16444            self.write_space();
16445            if self.config.unnest_with_ordinality {
16446                // Presto/Trino: UNNEST(arr) WITH ORDINALITY [AS alias]
16447                self.write_keyword("WITH ORDINALITY");
16448            } else if f.offset_alias.is_some() {
16449                // BigQuery: UNNEST(arr) [AS col] WITH OFFSET AS pos
16450                // Alias (if any) comes BEFORE WITH OFFSET
16451                if let Some(ref alias) = f.alias {
16452                    self.write_keyword("AS");
16453                    self.write_space();
16454                    self.generate_identifier(alias)?;
16455                    self.write_space();
16456                }
16457                self.write_keyword("WITH OFFSET");
16458                if let Some(ref offset_alias) = f.offset_alias {
16459                    self.write_space();
16460                    self.write_keyword("AS");
16461                    self.write_space();
16462                    self.generate_identifier(offset_alias)?;
16463                }
16464            } else {
16465                // WITH OFFSET (BigQuery identity) - add default "AS offset" if no explicit alias
16466                self.write_keyword("WITH OFFSET");
16467                if f.alias.is_none() {
16468                    self.write(" AS offset");
16469                }
16470            }
16471        }
16472        if let Some(ref alias) = f.alias {
16473            // Add alias for: non-WITH-OFFSET cases, Presto/Trino WITH ORDINALITY, or BigQuery WITH OFFSET + alias (no offset_alias)
16474            let should_add_alias = if !f.with_ordinality {
16475                true
16476            } else if self.config.unnest_with_ordinality {
16477                // Presto/Trino: alias comes after WITH ORDINALITY
16478                true
16479            } else if f.offset_alias.is_some() {
16480                // BigQuery expansion: alias already handled above
16481                false
16482            } else {
16483                // BigQuery WITH OFFSET + alias but no offset_alias: alias comes after
16484                true
16485            };
16486            if should_add_alias {
16487                self.write_space();
16488                self.write_keyword("AS");
16489                self.write_space();
16490                self.generate_identifier(alias)?;
16491            }
16492        }
16493        Ok(())
16494    }
16495
16496    fn generate_array_filter(&mut self, f: &ArrayFilterFunc) -> Result<()> {
16497        self.write_keyword("FILTER");
16498        self.write("(");
16499        self.generate_expression(&f.this)?;
16500        self.write(", ");
16501        self.generate_expression(&f.filter)?;
16502        self.write(")");
16503        Ok(())
16504    }
16505
16506    fn generate_array_transform(&mut self, f: &ArrayTransformFunc) -> Result<()> {
16507        self.write_keyword("TRANSFORM");
16508        self.write("(");
16509        self.generate_expression(&f.this)?;
16510        self.write(", ");
16511        self.generate_expression(&f.transform)?;
16512        self.write(")");
16513        Ok(())
16514    }
16515
16516    fn generate_sequence(&mut self, name: &str, f: &SequenceFunc) -> Result<()> {
16517        self.write_keyword(name);
16518        self.write("(");
16519        self.generate_expression(&f.start)?;
16520        self.write(", ");
16521        self.generate_expression(&f.stop)?;
16522        if let Some(ref step) = f.step {
16523            self.write(", ");
16524            self.generate_expression(step)?;
16525        }
16526        self.write(")");
16527        Ok(())
16528    }
16529
16530    // Struct function generators
16531
16532    fn generate_struct_constructor(&mut self, f: &StructConstructor) -> Result<()> {
16533        self.write_keyword("STRUCT");
16534        self.write("(");
16535        for (i, (name, expr)) in f.fields.iter().enumerate() {
16536            if i > 0 { self.write(", "); }
16537            if let Some(ref id) = name {
16538                self.generate_identifier(id)?;
16539                self.write(" ");
16540                self.write_keyword("AS");
16541                self.write(" ");
16542            }
16543            self.generate_expression(expr)?;
16544        }
16545        self.write(")");
16546        Ok(())
16547    }
16548
16549    /// Convert BigQuery STRUCT function (parsed as Function with Alias args) to target dialect
16550    fn generate_struct_function_cross_dialect(&mut self, func: &Function) -> Result<()> {
16551        // Extract named/unnamed fields from function args
16552        // Args are either Alias(this=value, alias=name) for named or plain expressions for unnamed
16553        let mut names: Vec<Option<String>> = Vec::new();
16554        let mut values: Vec<&Expression> = Vec::new();
16555        let mut all_named = true;
16556
16557        for arg in &func.args {
16558            match arg {
16559                Expression::Alias(a) => {
16560                    names.push(Some(a.alias.name.clone()));
16561                    values.push(&a.this);
16562                }
16563                _ => {
16564                    names.push(None);
16565                    values.push(arg);
16566                    all_named = false;
16567                }
16568            }
16569        }
16570
16571        if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
16572            // DuckDB: {'name': value, ...} for named, {'_0': value, ...} for unnamed
16573            self.write("{");
16574            for (i, (name, value)) in names.iter().zip(values.iter()).enumerate() {
16575                if i > 0 { self.write(", "); }
16576                if let Some(n) = name {
16577                    self.write("'");
16578                    self.write(n);
16579                    self.write("'");
16580                } else {
16581                    self.write("'_");
16582                    self.write(&i.to_string());
16583                    self.write("'");
16584                }
16585                self.write(": ");
16586                self.generate_expression(value)?;
16587            }
16588            self.write("}");
16589            return Ok(());
16590        }
16591
16592        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
16593            // Snowflake: OBJECT_CONSTRUCT('name', value, ...)
16594            self.write_keyword("OBJECT_CONSTRUCT");
16595            self.write("(");
16596            for (i, (name, value)) in names.iter().zip(values.iter()).enumerate() {
16597                if i > 0 { self.write(", "); }
16598                if let Some(n) = name {
16599                    self.write("'");
16600                    self.write(n);
16601                    self.write("'");
16602                } else {
16603                    self.write("'_");
16604                    self.write(&i.to_string());
16605                    self.write("'");
16606                }
16607                self.write(", ");
16608                self.generate_expression(value)?;
16609            }
16610            self.write(")");
16611            return Ok(());
16612        }
16613
16614        if matches!(self.config.dialect, Some(DialectType::Presto) | Some(DialectType::Trino)) {
16615            if all_named && !names.is_empty() {
16616                // Presto/Trino: CAST(ROW(values...) AS ROW(name TYPE, ...))
16617                // Need to infer types from values
16618                self.write_keyword("CAST");
16619                self.write("(");
16620                self.write_keyword("ROW");
16621                self.write("(");
16622                for (i, value) in values.iter().enumerate() {
16623                    if i > 0 { self.write(", "); }
16624                    self.generate_expression(value)?;
16625                }
16626                self.write(")");
16627                self.write(" ");
16628                self.write_keyword("AS");
16629                self.write(" ");
16630                self.write_keyword("ROW");
16631                self.write("(");
16632                for (i, (name, value)) in names.iter().zip(values.iter()).enumerate() {
16633                    if i > 0 { self.write(", "); }
16634                    if let Some(n) = name {
16635                        self.write(n);
16636                    }
16637                    self.write(" ");
16638                    let type_str = Self::infer_sql_type_for_presto(value);
16639                    self.write_keyword(&type_str);
16640                }
16641                self.write(")");
16642                self.write(")");
16643            } else {
16644                // Unnamed: ROW(values...)
16645                self.write_keyword("ROW");
16646                self.write("(");
16647                for (i, value) in values.iter().enumerate() {
16648                    if i > 0 { self.write(", "); }
16649                    self.generate_expression(value)?;
16650                }
16651                self.write(")");
16652            }
16653            return Ok(());
16654        }
16655
16656        // Default: ROW(values...) for other dialects
16657        self.write_keyword("ROW");
16658        self.write("(");
16659        for (i, value) in values.iter().enumerate() {
16660            if i > 0 { self.write(", "); }
16661            self.generate_expression(value)?;
16662        }
16663        self.write(")");
16664        Ok(())
16665    }
16666
16667    /// Infer SQL type name for a Presto/Trino ROW CAST from a literal expression
16668    fn infer_sql_type_for_presto(expr: &Expression) -> String {
16669        match expr {
16670            Expression::Literal(crate::expressions::Literal::String(_)) => "VARCHAR".to_string(),
16671            Expression::Literal(crate::expressions::Literal::Number(n)) => {
16672                if n.contains('.') { "DOUBLE".to_string() } else { "INTEGER".to_string() }
16673            }
16674            Expression::Boolean(_) => "BOOLEAN".to_string(),
16675            Expression::Literal(crate::expressions::Literal::Date(_)) => "DATE".to_string(),
16676            Expression::Literal(crate::expressions::Literal::Timestamp(_)) => "TIMESTAMP".to_string(),
16677            Expression::Literal(crate::expressions::Literal::Datetime(_)) => "TIMESTAMP".to_string(),
16678            Expression::Array(_) | Expression::ArrayFunc(_) => {
16679                // Try to infer element type from first element
16680                "ARRAY(VARCHAR)".to_string()
16681            }
16682            // For nested structs - generate a nested ROW type by inspecting fields
16683            Expression::Struct(_) | Expression::StructFunc(_) => "ROW".to_string(),
16684            Expression::Function(f) => {
16685                let up = f.name.to_uppercase();
16686                if up == "STRUCT" { "ROW".to_string() }
16687                else if up == "CURRENT_DATE" { "DATE".to_string() }
16688                else if up == "CURRENT_TIMESTAMP" || up == "NOW" { "TIMESTAMP".to_string() }
16689                else { "VARCHAR".to_string() }
16690            }
16691            _ => "VARCHAR".to_string(),
16692        }
16693    }
16694
16695    fn generate_struct_extract(&mut self, f: &StructExtractFunc) -> Result<()> {
16696        // DuckDB uses STRUCT_EXTRACT function syntax
16697        if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
16698            self.write_keyword("STRUCT_EXTRACT");
16699            self.write("(");
16700            self.generate_expression(&f.this)?;
16701            self.write(", ");
16702            // Output field name as string literal
16703            self.write("'");
16704            self.write(&f.field.name);
16705            self.write("'");
16706            self.write(")");
16707            return Ok(());
16708        }
16709        self.generate_expression(&f.this)?;
16710        self.write(".");
16711        self.generate_identifier(&f.field)
16712    }
16713
16714    fn generate_named_struct(&mut self, f: &NamedStructFunc) -> Result<()> {
16715        self.write_keyword("NAMED_STRUCT");
16716        self.write("(");
16717        for (i, (name, value)) in f.pairs.iter().enumerate() {
16718            if i > 0 { self.write(", "); }
16719            self.generate_expression(name)?;
16720            self.write(", ");
16721            self.generate_expression(value)?;
16722        }
16723        self.write(")");
16724        Ok(())
16725    }
16726
16727    // Map function generators
16728
16729    fn generate_map_constructor(&mut self, f: &MapConstructor) -> Result<()> {
16730        if f.curly_brace_syntax {
16731            // Curly brace syntax: MAP {'a': 1, 'b': 2} or just {'a': 1, 'b': 2}
16732            if f.with_map_keyword {
16733                self.write_keyword("MAP");
16734                self.write(" ");
16735            }
16736            self.write("{");
16737            for (i, (key, val)) in f.keys.iter().zip(f.values.iter()).enumerate() {
16738                if i > 0 { self.write(", "); }
16739                self.generate_expression(key)?;
16740                self.write(": ");
16741                self.generate_expression(val)?;
16742            }
16743            self.write("}");
16744        } else {
16745            // MAP function syntax: MAP(ARRAY[keys], ARRAY[values])
16746            self.write_keyword("MAP");
16747            self.write("(");
16748            self.write_keyword("ARRAY");
16749            self.write("[");
16750            for (i, key) in f.keys.iter().enumerate() {
16751                if i > 0 { self.write(", "); }
16752                self.generate_expression(key)?;
16753            }
16754            self.write("], ");
16755            self.write_keyword("ARRAY");
16756            self.write("[");
16757            for (i, val) in f.values.iter().enumerate() {
16758                if i > 0 { self.write(", "); }
16759                self.generate_expression(val)?;
16760            }
16761            self.write("])");
16762        }
16763        Ok(())
16764    }
16765
16766    fn generate_transform_func(&mut self, name: &str, f: &TransformFunc) -> Result<()> {
16767        self.write_keyword(name);
16768        self.write("(");
16769        self.generate_expression(&f.this)?;
16770        self.write(", ");
16771        self.generate_expression(&f.transform)?;
16772        self.write(")");
16773        Ok(())
16774    }
16775
16776    // JSON function generators
16777
16778    fn generate_json_extract(&mut self, name: &str, f: &JsonExtractFunc) -> Result<()> {
16779        use crate::dialects::DialectType;
16780
16781        // Check if we should use arrow syntax (-> or ->>)
16782        let use_arrow = f.arrow_syntax && self.dialect_supports_json_arrow();
16783
16784        if use_arrow {
16785            // Output arrow syntax: expr -> path or expr ->> path
16786            self.generate_expression(&f.this)?;
16787            if name == "JSON_EXTRACT_SCALAR" || name == "JSON_EXTRACT_PATH_TEXT" {
16788                self.write(" ->> ");
16789            } else {
16790                self.write(" -> ");
16791            }
16792            self.generate_expression(&f.path)?;
16793            return Ok(());
16794        }
16795
16796        // PostgreSQL uses #>> operator for JSONB path text extraction (only when hash_arrow_syntax is true)
16797        if f.hash_arrow_syntax && matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
16798            self.generate_expression(&f.this)?;
16799            self.write(" #>> ");
16800            self.generate_expression(&f.path)?;
16801            return Ok(());
16802        }
16803
16804        // For PostgreSQL/Redshift, use JSON_EXTRACT_PATH / JSON_EXTRACT_PATH_TEXT for extraction without arrow syntax
16805        // Redshift maps everything to JSON_EXTRACT_PATH_TEXT since it doesn't have JSON_EXTRACT_PATH
16806        let func_name = if matches!(self.config.dialect, Some(DialectType::Redshift)) {
16807            match name {
16808                "JSON_EXTRACT_SCALAR" | "JSON_EXTRACT_PATH_TEXT" | "JSON_EXTRACT" | "JSON_EXTRACT_PATH" => "JSON_EXTRACT_PATH_TEXT",
16809                _ => name,
16810            }
16811        } else if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) {
16812            match name {
16813                "JSON_EXTRACT_SCALAR" | "JSON_EXTRACT_PATH_TEXT" => "JSON_EXTRACT_PATH_TEXT",
16814                "JSON_EXTRACT" | "JSON_EXTRACT_PATH" => "JSON_EXTRACT_PATH",
16815                _ => name,
16816            }
16817        } else {
16818            name
16819        };
16820
16821        self.write_keyword(func_name);
16822        self.write("(");
16823        // For Redshift, strip CAST(... AS JSON) wrapper from the expression
16824        if matches!(self.config.dialect, Some(DialectType::Redshift)) {
16825            if let Expression::Cast(ref cast) = f.this {
16826                if matches!(cast.to, crate::expressions::DataType::Json) {
16827                    self.generate_expression(&cast.this)?;
16828                } else {
16829                    self.generate_expression(&f.this)?;
16830                }
16831            } else {
16832                self.generate_expression(&f.this)?;
16833            }
16834        } else {
16835            self.generate_expression(&f.this)?;
16836        }
16837        self.write(", ");
16838        self.generate_expression(&f.path)?;
16839
16840        // Output JSON_QUERY/JSON_VALUE options (Trino/Presto style)
16841        // These go BEFORE the closing parenthesis
16842        if let Some(ref wrapper) = f.wrapper_option {
16843            self.write_space();
16844            self.write_keyword(wrapper);
16845        }
16846        if let Some(ref quotes) = f.quotes_option {
16847            self.write_space();
16848            self.write_keyword(quotes);
16849            if f.on_scalar_string {
16850                self.write_space();
16851                self.write_keyword("ON SCALAR STRING");
16852            }
16853        }
16854        if let Some(ref on_err) = f.on_error {
16855            self.write_space();
16856            self.write_keyword(on_err);
16857        }
16858        if let Some(ref ret_type) = f.returning {
16859            self.write_space();
16860            self.write_keyword("RETURNING");
16861            self.write_space();
16862            self.generate_data_type(ret_type)?;
16863        }
16864
16865        self.write(")");
16866        Ok(())
16867    }
16868
16869    /// Check if the current dialect supports JSON arrow operators (-> and ->>)
16870    fn dialect_supports_json_arrow(&self) -> bool {
16871        use crate::dialects::DialectType;
16872        match self.config.dialect {
16873            // PostgreSQL, MySQL, DuckDB support -> and ->> operators
16874            Some(DialectType::PostgreSQL) => true,
16875            Some(DialectType::MySQL) => true,
16876            Some(DialectType::DuckDB) => true,
16877            Some(DialectType::CockroachDB) => true,
16878            Some(DialectType::StarRocks) => true,
16879            Some(DialectType::SQLite) => true,
16880            // Other dialects use function syntax
16881            _ => false,
16882        }
16883    }
16884
16885    fn generate_json_path(&mut self, name: &str, f: &JsonPathFunc) -> Result<()> {
16886        use crate::dialects::DialectType;
16887
16888        // PostgreSQL uses #> operator for JSONB path extraction
16889        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) && name == "JSON_EXTRACT_PATH" {
16890            self.generate_expression(&f.this)?;
16891            self.write(" #> ");
16892            if f.paths.len() == 1 {
16893                self.generate_expression(&f.paths[0])?;
16894            } else {
16895                // Multiple paths: ARRAY[path1, path2, ...]
16896                self.write_keyword("ARRAY");
16897                self.write("[");
16898                for (i, path) in f.paths.iter().enumerate() {
16899                    if i > 0 { self.write(", "); }
16900                    self.generate_expression(path)?;
16901                }
16902                self.write("]");
16903            }
16904            return Ok(());
16905        }
16906
16907        self.write_keyword(name);
16908        self.write("(");
16909        self.generate_expression(&f.this)?;
16910        for path in &f.paths {
16911            self.write(", ");
16912            self.generate_expression(path)?;
16913        }
16914        self.write(")");
16915        Ok(())
16916    }
16917
16918    fn generate_json_object(&mut self, f: &JsonObjectFunc) -> Result<()> {
16919        use crate::dialects::DialectType;
16920
16921        self.write_keyword("JSON_OBJECT");
16922        self.write("(");
16923        if f.star {
16924            self.write("*");
16925        } else {
16926            // BigQuery, MySQL, and SQLite use comma syntax: JSON_OBJECT('key', value)
16927            // Standard SQL uses colon syntax: JSON_OBJECT('key': value)
16928            // Also respect the json_key_value_pair_sep config
16929            let use_comma_syntax = self.config.json_key_value_pair_sep == "," ||
16930                matches!(self.config.dialect, Some(DialectType::BigQuery) | Some(DialectType::MySQL) | Some(DialectType::SQLite));
16931
16932            for (i, (key, value)) in f.pairs.iter().enumerate() {
16933                if i > 0 { self.write(", "); }
16934                self.generate_expression(key)?;
16935                if use_comma_syntax {
16936                    self.write(", ");
16937                } else {
16938                    self.write(": ");
16939                }
16940                self.generate_expression(value)?;
16941            }
16942        }
16943        if let Some(null_handling) = f.null_handling {
16944            self.write_space();
16945            match null_handling {
16946                JsonNullHandling::NullOnNull => self.write_keyword("NULL ON NULL"),
16947                JsonNullHandling::AbsentOnNull => self.write_keyword("ABSENT ON NULL"),
16948            }
16949        }
16950        if f.with_unique_keys {
16951            self.write_space();
16952            self.write_keyword("WITH UNIQUE KEYS");
16953        }
16954        if let Some(ref ret_type) = f.returning_type {
16955            self.write_space();
16956            self.write_keyword("RETURNING");
16957            self.write_space();
16958            self.generate_data_type(ret_type)?;
16959            if f.format_json {
16960                self.write_space();
16961                self.write_keyword("FORMAT JSON");
16962            }
16963            if let Some(ref enc) = f.encoding {
16964                self.write_space();
16965                self.write_keyword("ENCODING");
16966                self.write_space();
16967                self.write(enc);
16968            }
16969        }
16970        self.write(")");
16971        Ok(())
16972    }
16973
16974    fn generate_json_modify(&mut self, name: &str, f: &JsonModifyFunc) -> Result<()> {
16975        self.write_keyword(name);
16976        self.write("(");
16977        self.generate_expression(&f.this)?;
16978        for (path, value) in &f.path_values {
16979            self.write(", ");
16980            self.generate_expression(path)?;
16981            self.write(", ");
16982            self.generate_expression(value)?;
16983        }
16984        self.write(")");
16985        Ok(())
16986    }
16987
16988    fn generate_json_array_agg(&mut self, f: &JsonArrayAggFunc) -> Result<()> {
16989        self.write_keyword("JSON_ARRAYAGG");
16990        self.write("(");
16991        self.generate_expression(&f.this)?;
16992        if let Some(ref order_by) = f.order_by {
16993            self.write_space();
16994            self.write_keyword("ORDER BY");
16995            self.write_space();
16996            for (i, ord) in order_by.iter().enumerate() {
16997                if i > 0 { self.write(", "); }
16998                self.generate_ordered(ord)?;
16999            }
17000        }
17001        if let Some(null_handling) = f.null_handling {
17002            self.write_space();
17003            match null_handling {
17004                JsonNullHandling::NullOnNull => self.write_keyword("NULL ON NULL"),
17005                JsonNullHandling::AbsentOnNull => self.write_keyword("ABSENT ON NULL"),
17006            }
17007        }
17008        self.write(")");
17009        if let Some(ref filter) = f.filter {
17010            self.write_space();
17011            self.write_keyword("FILTER");
17012            self.write("(");
17013            self.write_keyword("WHERE");
17014            self.write_space();
17015            self.generate_expression(filter)?;
17016            self.write(")");
17017        }
17018        Ok(())
17019    }
17020
17021    fn generate_json_object_agg(&mut self, f: &JsonObjectAggFunc) -> Result<()> {
17022        self.write_keyword("JSON_OBJECTAGG");
17023        self.write("(");
17024        self.generate_expression(&f.key)?;
17025        self.write(": ");
17026        self.generate_expression(&f.value)?;
17027        if let Some(null_handling) = f.null_handling {
17028            self.write_space();
17029            match null_handling {
17030                JsonNullHandling::NullOnNull => self.write_keyword("NULL ON NULL"),
17031                JsonNullHandling::AbsentOnNull => self.write_keyword("ABSENT ON NULL"),
17032            }
17033        }
17034        self.write(")");
17035        if let Some(ref filter) = f.filter {
17036            self.write_space();
17037            self.write_keyword("FILTER");
17038            self.write("(");
17039            self.write_keyword("WHERE");
17040            self.write_space();
17041            self.generate_expression(filter)?;
17042            self.write(")");
17043        }
17044        Ok(())
17045    }
17046
17047    // Type casting/conversion generators
17048
17049    fn generate_convert(&mut self, f: &ConvertFunc) -> Result<()> {
17050        use crate::dialects::DialectType;
17051
17052        // Redshift: CONVERT(type, expr) -> CAST(expr AS type)
17053        if self.config.dialect == Some(DialectType::Redshift) {
17054            self.write_keyword("CAST");
17055            self.write("(");
17056            self.generate_expression(&f.this)?;
17057            self.write_space();
17058            self.write_keyword("AS");
17059            self.write_space();
17060            self.generate_data_type(&f.to)?;
17061            self.write(")");
17062            return Ok(());
17063        }
17064
17065        self.write_keyword("CONVERT");
17066        self.write("(");
17067        self.generate_data_type(&f.to)?;
17068        self.write(", ");
17069        self.generate_expression(&f.this)?;
17070        if let Some(ref style) = f.style {
17071            self.write(", ");
17072            self.generate_expression(style)?;
17073        }
17074        self.write(")");
17075        Ok(())
17076    }
17077
17078    // Additional expression generators
17079
17080    fn generate_lambda(&mut self, f: &LambdaExpr) -> Result<()> {
17081        if f.colon {
17082            // DuckDB syntax: LAMBDA x : expr
17083            self.write_keyword("LAMBDA");
17084            self.write_space();
17085            for (i, param) in f.parameters.iter().enumerate() {
17086                if i > 0 { self.write(", "); }
17087                self.generate_identifier(param)?;
17088            }
17089            self.write(" : ");
17090        } else {
17091            // Standard syntax: x -> expr or (x, y) -> expr
17092            if f.parameters.len() == 1 {
17093                self.generate_identifier(&f.parameters[0])?;
17094            } else {
17095                self.write("(");
17096                for (i, param) in f.parameters.iter().enumerate() {
17097                    if i > 0 { self.write(", "); }
17098                    self.generate_identifier(param)?;
17099                }
17100                self.write(")");
17101            }
17102            self.write(" -> ");
17103        }
17104        self.generate_expression(&f.body)
17105    }
17106
17107    fn generate_named_argument(&mut self, f: &NamedArgument) -> Result<()> {
17108        self.generate_identifier(&f.name)?;
17109        match f.separator {
17110            NamedArgSeparator::DArrow => self.write(" => "),
17111            NamedArgSeparator::ColonEq => self.write(" := "),
17112            NamedArgSeparator::Eq => self.write(" = "),
17113        }
17114        self.generate_expression(&f.value)
17115    }
17116
17117    fn generate_table_argument(&mut self, f: &TableArgument) -> Result<()> {
17118        self.write_keyword(&f.prefix);
17119        self.write(" ");
17120        self.generate_expression(&f.this)
17121    }
17122
17123    fn generate_parameter(&mut self, f: &Parameter) -> Result<()> {
17124        match f.style {
17125            ParameterStyle::Question => self.write("?"),
17126            ParameterStyle::Dollar => {
17127                self.write("$");
17128                if let Some(idx) = f.index {
17129                    self.write(&idx.to_string());
17130                } else if let Some(ref name) = f.name {
17131                    // Session variable like $x or $query_id
17132                    self.write(name);
17133                }
17134            }
17135            ParameterStyle::DollarBrace => {
17136                // Template variable like ${x} or ${hiveconf:name} (Databricks, Hive)
17137                self.write("${");
17138                if let Some(ref name) = f.name {
17139                    self.write(name);
17140                }
17141                if let Some(ref expr) = f.expression {
17142                    self.write(":");
17143                    self.write(expr);
17144                }
17145                self.write("}");
17146            }
17147            ParameterStyle::Colon => {
17148                self.write(":");
17149                if let Some(idx) = f.index {
17150                    self.write(&idx.to_string());
17151                } else if let Some(ref name) = f.name {
17152                    self.write(name);
17153                }
17154            }
17155            ParameterStyle::At => {
17156                self.write("@");
17157                if let Some(ref name) = f.name {
17158                    if f.quoted {
17159                        self.write("\"");
17160                        self.write(name);
17161                        self.write("\"");
17162                    } else {
17163                        self.write(name);
17164                    }
17165                }
17166            }
17167            ParameterStyle::DoubleAt => {
17168                self.write("@@");
17169                if let Some(ref name) = f.name {
17170                    self.write(name);
17171                }
17172            }
17173            ParameterStyle::DoubleDollar => {
17174                self.write("$$");
17175                if let Some(ref name) = f.name {
17176                    self.write(name);
17177                }
17178            }
17179            ParameterStyle::Percent => {
17180                if let Some(ref name) = f.name {
17181                    // %(name)s format
17182                    self.write("%(");
17183                    self.write(name);
17184                    self.write(")s");
17185                } else {
17186                    // %s format
17187                    self.write("%s");
17188                }
17189            }
17190            ParameterStyle::Brace => {
17191                // Spark/Databricks widget template variable: {name}
17192                // ClickHouse query parameter may include kind: {name: Type}
17193                self.write("{");
17194                if let Some(ref name) = f.name {
17195                    self.write(name);
17196                }
17197                if let Some(ref expr) = f.expression {
17198                    self.write(": ");
17199                    self.write(expr);
17200                }
17201                self.write("}");
17202            }
17203        }
17204        Ok(())
17205    }
17206
17207    fn generate_placeholder(&mut self, f: &Placeholder) -> Result<()> {
17208        self.write("?");
17209        if let Some(idx) = f.index {
17210            self.write(&idx.to_string());
17211        }
17212        Ok(())
17213    }
17214
17215    fn generate_sql_comment(&mut self, f: &SqlComment) -> Result<()> {
17216        if f.is_block {
17217            self.write("/*");
17218            self.write(&f.text);
17219            self.write("*/");
17220        } else {
17221            self.write("--");
17222            self.write(&f.text);
17223        }
17224        Ok(())
17225    }
17226
17227    // Additional predicate generators
17228
17229    fn generate_similar_to(&mut self, f: &SimilarToExpr) -> Result<()> {
17230        self.generate_expression(&f.this)?;
17231        if f.not {
17232            self.write_space();
17233            self.write_keyword("NOT");
17234        }
17235        self.write_space();
17236        self.write_keyword("SIMILAR TO");
17237        self.write_space();
17238        self.generate_expression(&f.pattern)?;
17239        if let Some(ref escape) = f.escape {
17240            self.write_space();
17241            self.write_keyword("ESCAPE");
17242            self.write_space();
17243            self.generate_expression(escape)?;
17244        }
17245        Ok(())
17246    }
17247
17248    fn generate_quantified(&mut self, name: &str, f: &QuantifiedExpr) -> Result<()> {
17249        self.generate_expression(&f.this)?;
17250        self.write_space();
17251        // Output comparison operator if present
17252        if let Some(op) = &f.op {
17253            match op {
17254                QuantifiedOp::Eq => self.write("="),
17255                QuantifiedOp::Neq => self.write("<>"),
17256                QuantifiedOp::Lt => self.write("<"),
17257                QuantifiedOp::Lte => self.write("<="),
17258                QuantifiedOp::Gt => self.write(">"),
17259                QuantifiedOp::Gte => self.write(">="),
17260            }
17261            self.write_space();
17262        }
17263        self.write_keyword(name);
17264        if self.config.quantified_no_paren_space {
17265            self.write("(");
17266        } else {
17267            self.write(" (");
17268        }
17269        self.generate_expression(&f.subquery)?;
17270        self.write(")");
17271        Ok(())
17272    }
17273
17274    fn generate_overlaps(&mut self, f: &OverlapsExpr) -> Result<()> {
17275        // Check if this is a simple binary form (this OVERLAPS expression)
17276        if let (Some(this), Some(expr)) = (&f.this, &f.expression) {
17277            self.generate_expression(this)?;
17278            self.write_space();
17279            self.write_keyword("OVERLAPS");
17280            self.write_space();
17281            self.generate_expression(expr)?;
17282        } else if let (Some(ls), Some(le), Some(rs), Some(re)) =
17283            (&f.left_start, &f.left_end, &f.right_start, &f.right_end)
17284        {
17285            // Full ANSI form: (a, b) OVERLAPS (c, d)
17286            self.write("(");
17287            self.generate_expression(ls)?;
17288            self.write(", ");
17289            self.generate_expression(le)?;
17290            self.write(")");
17291            self.write_space();
17292            self.write_keyword("OVERLAPS");
17293            self.write_space();
17294            self.write("(");
17295            self.generate_expression(rs)?;
17296            self.write(", ");
17297            self.generate_expression(re)?;
17298            self.write(")");
17299        }
17300        Ok(())
17301    }
17302
17303    // Type conversion generators
17304
17305    fn generate_try_cast(&mut self, cast: &Cast) -> Result<()> {
17306        use crate::dialects::DialectType;
17307
17308        // SingleStore uses !:> syntax for try cast
17309        if matches!(self.config.dialect, Some(DialectType::SingleStore)) {
17310            self.generate_expression(&cast.this)?;
17311            self.write(" !:> ");
17312            self.generate_data_type(&cast.to)?;
17313            return Ok(());
17314        }
17315
17316        // Teradata uses TRYCAST (no underscore)
17317        if matches!(self.config.dialect, Some(DialectType::Teradata)) {
17318            self.write_keyword("TRYCAST");
17319            self.write("(");
17320            self.generate_expression(&cast.this)?;
17321            self.write_space();
17322            self.write_keyword("AS");
17323            self.write_space();
17324            self.generate_data_type(&cast.to)?;
17325            self.write(")");
17326            return Ok(());
17327        }
17328
17329        // Dialects without TRY_CAST: generate as regular CAST
17330        let keyword = if matches!(self.config.dialect,
17331            Some(DialectType::Hive)
17332            | Some(DialectType::MySQL) | Some(DialectType::SQLite) | Some(DialectType::Oracle)
17333            | Some(DialectType::ClickHouse)
17334            | Some(DialectType::Redshift) | Some(DialectType::PostgreSQL)
17335            | Some(DialectType::StarRocks) | Some(DialectType::Doris)
17336        ) {
17337            "CAST"
17338        } else {
17339            "TRY_CAST"
17340        };
17341
17342        self.write_keyword(keyword);
17343        self.write("(");
17344        self.generate_expression(&cast.this)?;
17345        self.write_space();
17346        self.write_keyword("AS");
17347        self.write_space();
17348        self.generate_data_type(&cast.to)?;
17349
17350        // Output FORMAT clause if present
17351        if let Some(format) = &cast.format {
17352            self.write_space();
17353            self.write_keyword("FORMAT");
17354            self.write_space();
17355            self.generate_expression(format)?;
17356        }
17357
17358        self.write(")");
17359        Ok(())
17360    }
17361
17362    fn generate_safe_cast(&mut self, cast: &Cast) -> Result<()> {
17363        self.write_keyword("SAFE_CAST");
17364        self.write("(");
17365        self.generate_expression(&cast.this)?;
17366        self.write_space();
17367        self.write_keyword("AS");
17368        self.write_space();
17369        self.generate_data_type(&cast.to)?;
17370
17371        // Output FORMAT clause if present
17372        if let Some(format) = &cast.format {
17373            self.write_space();
17374            self.write_keyword("FORMAT");
17375            self.write_space();
17376            self.generate_expression(format)?;
17377        }
17378
17379        self.write(")");
17380        Ok(())
17381    }
17382
17383    // Array/struct/map access generators
17384
17385    fn generate_subscript(&mut self, s: &Subscript) -> Result<()> {
17386        self.generate_expression(&s.this)?;
17387        self.write("[");
17388        self.generate_expression(&s.index)?;
17389        self.write("]");
17390        Ok(())
17391    }
17392
17393    fn generate_dot_access(&mut self, d: &DotAccess) -> Result<()> {
17394        self.generate_expression(&d.this)?;
17395        // Snowflake uses : (colon) for first-level struct/object field access on CAST/column expressions
17396        // e.g., CAST(col AS OBJECT(fld1 OBJECT(fld2 INT))):fld1.fld2
17397        let use_colon = matches!(self.config.dialect, Some(DialectType::Snowflake))
17398            && matches!(&d.this, Expression::Cast(_) | Expression::SafeCast(_) | Expression::TryCast(_));
17399        if use_colon {
17400            self.write(":");
17401        } else {
17402            self.write(".");
17403        }
17404        self.generate_identifier(&d.field)
17405    }
17406
17407    fn generate_method_call(&mut self, m: &MethodCall) -> Result<()> {
17408        self.generate_expression(&m.this)?;
17409        self.write(".");
17410        // Method names after a dot should not be quoted based on reserved keywords
17411        // Only quote if explicitly marked as quoted in the AST
17412        if m.method.quoted {
17413            let q = self.config.identifier_quote;
17414            self.write(&format!("{}{}{}", q, m.method.name, q));
17415        } else {
17416            self.write(&m.method.name);
17417        }
17418        self.write("(");
17419        for (i, arg) in m.args.iter().enumerate() {
17420            if i > 0 {
17421                self.write(", ");
17422            }
17423            self.generate_expression(arg)?;
17424        }
17425        self.write(")");
17426        Ok(())
17427    }
17428
17429    fn generate_array_slice(&mut self, s: &ArraySlice) -> Result<()> {
17430        // Check if we need to wrap the inner expression in parentheses
17431        // JSON arrow expressions have lower precedence than array subscript
17432        let needs_parens = matches!(
17433            &s.this,
17434            Expression::JsonExtract(f) if f.arrow_syntax
17435        ) || matches!(
17436            &s.this,
17437            Expression::JsonExtractScalar(f) if f.arrow_syntax
17438        );
17439
17440        if needs_parens {
17441            self.write("(");
17442        }
17443        self.generate_expression(&s.this)?;
17444        if needs_parens {
17445            self.write(")");
17446        }
17447        self.write("[");
17448        if let Some(start) = &s.start {
17449            self.generate_expression(start)?;
17450        }
17451        self.write(":");
17452        if let Some(end) = &s.end {
17453            self.generate_expression(end)?;
17454        }
17455        self.write("]");
17456        Ok(())
17457    }
17458
17459
17460    fn generate_binary_op(&mut self, op: &BinaryOp, operator: &str) -> Result<()> {
17461        // Generate left expression, but skip trailing comments if they're already in left_comments
17462        // to avoid duplication (comments are captured as both expr.trailing_comments
17463        // and BinaryOp.left_comments during parsing)
17464        match &op.left {
17465            Expression::Column(col) => {
17466                // Generate column without trailing comments but include join_mark
17467                if let Some(table) = &col.table {
17468                    self.generate_identifier(table)?;
17469                    self.write(".");
17470                }
17471                self.generate_identifier(&col.name)?;
17472                // Oracle-style join marker (+)
17473                if col.join_mark && self.config.supports_column_join_marks {
17474                    self.write(" (+)");
17475                }
17476            }
17477            Expression::Add(inner_op) | Expression::Sub(inner_op) |
17478            Expression::Mul(inner_op) | Expression::Div(inner_op) |
17479            Expression::Concat(inner_op) => {
17480                // Generate binary op without its trailing comments
17481                self.generate_binary_op_no_trailing(inner_op, match &op.left {
17482                    Expression::Add(_) => "+",
17483                    Expression::Sub(_) => "-",
17484                    Expression::Mul(_) => "*",
17485                    Expression::Div(_) => "/",
17486                    Expression::Concat(_) => "||",
17487                    _ => unreachable!("op.left variant already matched by outer arm as Add/Sub/Mul/Div/Concat"),
17488                })?;
17489            }
17490            _ => {
17491                self.generate_expression(&op.left)?;
17492            }
17493        }
17494        // Output comments after left operand
17495        for comment in &op.left_comments {
17496            self.write_space();
17497            self.write(comment);
17498        }
17499        if self.config.pretty
17500            && matches!(self.config.dialect, Some(DialectType::Snowflake))
17501            && (operator == "AND" || operator == "OR")
17502        {
17503            self.write_newline();
17504            self.write_indent();
17505            self.write_keyword(operator);
17506        } else {
17507            self.write_space();
17508            if operator.chars().all(|c| c.is_alphabetic()) {
17509                self.write_keyword(operator);
17510            } else {
17511                self.write(operator);
17512            }
17513        }
17514        // Output comments after operator (before right operand)
17515        for comment in &op.operator_comments {
17516            self.write_space();
17517            self.write(comment);
17518        }
17519        self.write_space();
17520        self.generate_expression(&op.right)?;
17521        // Output trailing comments after right operand
17522        for comment in &op.trailing_comments {
17523            self.write_space();
17524            self.write(comment);
17525        }
17526        Ok(())
17527    }
17528
17529    /// Generate LIKE/ILIKE operation with optional ESCAPE clause
17530    fn generate_like_op(&mut self, op: &LikeOp, operator: &str) -> Result<()> {
17531        self.generate_expression(&op.left)?;
17532        self.write_space();
17533        self.write_keyword(operator);
17534        if let Some(quantifier) = &op.quantifier {
17535            self.write_space();
17536            self.write_keyword(quantifier);
17537        }
17538        self.write_space();
17539        self.generate_expression(&op.right)?;
17540        if let Some(escape) = &op.escape {
17541            self.write_space();
17542            self.write_keyword("ESCAPE");
17543            self.write_space();
17544            self.generate_expression(escape)?;
17545        }
17546        Ok(())
17547    }
17548
17549    /// Generate null-safe equality
17550    /// MySQL uses <=>, other dialects use IS NOT DISTINCT FROM
17551    fn generate_null_safe_eq(&mut self, op: &BinaryOp) -> Result<()> {
17552        use crate::dialects::DialectType;
17553        self.generate_expression(&op.left)?;
17554        self.write_space();
17555        if matches!(self.config.dialect, Some(DialectType::MySQL)) {
17556            self.write("<=>");
17557        } else {
17558            self.write_keyword("IS NOT DISTINCT FROM");
17559        }
17560        self.write_space();
17561        self.generate_expression(&op.right)?;
17562        Ok(())
17563    }
17564
17565    /// Generate IS DISTINCT FROM (null-safe inequality)
17566    fn generate_null_safe_neq(&mut self, op: &BinaryOp) -> Result<()> {
17567        self.generate_expression(&op.left)?;
17568        self.write_space();
17569        self.write_keyword("IS DISTINCT FROM");
17570        self.write_space();
17571        self.generate_expression(&op.right)?;
17572        Ok(())
17573    }
17574
17575    /// Generate binary op without trailing comments (used when nested inside another binary op)
17576    fn generate_binary_op_no_trailing(&mut self, op: &BinaryOp, operator: &str) -> Result<()> {
17577        // Generate left expression, but skip trailing comments
17578        match &op.left {
17579            Expression::Column(col) => {
17580                if let Some(table) = &col.table {
17581                    self.generate_identifier(table)?;
17582                    self.write(".");
17583                }
17584                self.generate_identifier(&col.name)?;
17585                // Oracle-style join marker (+)
17586                if col.join_mark && self.config.supports_column_join_marks {
17587                    self.write(" (+)");
17588                }
17589            }
17590            Expression::Add(inner_op) | Expression::Sub(inner_op) |
17591            Expression::Mul(inner_op) | Expression::Div(inner_op) |
17592            Expression::Concat(inner_op) => {
17593                self.generate_binary_op_no_trailing(inner_op, match &op.left {
17594                    Expression::Add(_) => "+",
17595                    Expression::Sub(_) => "-",
17596                    Expression::Mul(_) => "*",
17597                    Expression::Div(_) => "/",
17598                    Expression::Concat(_) => "||",
17599                    _ => unreachable!("op.left variant already matched by outer arm as Add/Sub/Mul/Div/Concat"),
17600                })?;
17601            }
17602            _ => {
17603                self.generate_expression(&op.left)?;
17604            }
17605        }
17606        // Output left_comments
17607        for comment in &op.left_comments {
17608            self.write_space();
17609            self.write(comment);
17610        }
17611        self.write_space();
17612        if operator.chars().all(|c| c.is_alphabetic()) {
17613            self.write_keyword(operator);
17614        } else {
17615            self.write(operator);
17616        }
17617        // Output operator_comments
17618        for comment in &op.operator_comments {
17619            self.write_space();
17620            self.write(comment);
17621        }
17622        self.write_space();
17623        // Generate right expression, but skip trailing comments if it's a Column
17624        // (the parent's left_comments will output them)
17625        match &op.right {
17626            Expression::Column(col) => {
17627                if let Some(table) = &col.table {
17628                    self.generate_identifier(table)?;
17629                    self.write(".");
17630                }
17631                self.generate_identifier(&col.name)?;
17632                // Oracle-style join marker (+)
17633                if col.join_mark && self.config.supports_column_join_marks {
17634                    self.write(" (+)");
17635                }
17636            }
17637            _ => {
17638                self.generate_expression(&op.right)?;
17639            }
17640        }
17641        // Skip trailing_comments - parent will handle them via its left_comments
17642        Ok(())
17643    }
17644
17645    fn generate_unary_op(&mut self, op: &UnaryOp, operator: &str) -> Result<()> {
17646        if operator.chars().all(|c| c.is_alphabetic()) {
17647            self.write_keyword(operator);
17648            self.write_space();
17649        } else {
17650            self.write(operator);
17651            // Add space between consecutive unary operators (e.g., "- -5" not "--5")
17652            if matches!(&op.this, Expression::Neg(_) | Expression::BitwiseNot(_)) {
17653                self.write_space();
17654            }
17655        }
17656        self.generate_expression(&op.this)
17657    }
17658
17659    fn generate_in(&mut self, in_expr: &In) -> Result<()> {
17660        self.generate_expression(&in_expr.this)?;
17661        if in_expr.global {
17662            self.write_space();
17663            self.write_keyword("GLOBAL");
17664        }
17665        if in_expr.not {
17666            self.write_space();
17667            self.write_keyword("NOT");
17668        }
17669        self.write_space();
17670        self.write_keyword("IN");
17671
17672        // BigQuery: IN UNNEST(expr)
17673        if let Some(unnest_expr) = &in_expr.unnest {
17674            self.write_space();
17675            self.write_keyword("UNNEST");
17676            self.write("(");
17677            self.generate_expression(unnest_expr)?;
17678            self.write(")");
17679            return Ok(());
17680        }
17681
17682        if let Some(query) = &in_expr.query {
17683            // Check if this is a bare identifier (PIVOT FOR foo IN y_enum)
17684            // vs a subquery (col IN (SELECT ...))
17685            let is_bare = in_expr.expressions.is_empty() && !matches!(
17686                query,
17687                Expression::Select(_) | Expression::Union(_) |
17688                Expression::Intersect(_) | Expression::Except(_) |
17689                Expression::Subquery(_)
17690            );
17691            if is_bare {
17692                // Bare identifier: no parentheses
17693                self.write_space();
17694                self.generate_expression(query)?;
17695            } else {
17696                // Subquery: with parentheses
17697                self.write(" (");
17698                let is_statement = matches!(
17699                    query,
17700                    Expression::Select(_) | Expression::Union(_) |
17701                    Expression::Intersect(_) | Expression::Except(_) |
17702                    Expression::Subquery(_)
17703                );
17704                if self.config.pretty && is_statement {
17705                    self.write_newline();
17706                    self.indent_level += 1;
17707                    self.write_indent();
17708                }
17709                self.generate_expression(query)?;
17710                if self.config.pretty && is_statement {
17711                    self.write_newline();
17712                    self.indent_level -= 1;
17713                    self.write_indent();
17714                }
17715                self.write(")");
17716            }
17717        } else {
17718            // DuckDB: IN without parentheses for single expression that is NOT a literal
17719            // (array/list membership like 'red' IN tbl.flags)
17720            // ClickHouse: IN without parentheses for single non-array expressions
17721            let is_duckdb = matches!(self.config.dialect, Some(crate::dialects::DialectType::DuckDB));
17722            let is_clickhouse = matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse));
17723            let single_expr = in_expr.expressions.len() == 1;
17724            if is_clickhouse && single_expr {
17725                if let Expression::Array(arr) = &in_expr.expressions[0] {
17726                    // ClickHouse: x IN [1, 2] -> x IN (1, 2)
17727                    self.write(" (");
17728                    for (i, expr) in arr.expressions.iter().enumerate() {
17729                        if i > 0 {
17730                            self.write(", ");
17731                        }
17732                        self.generate_expression(expr)?;
17733                    }
17734                    self.write(")");
17735                } else {
17736                    self.write_space();
17737                    self.generate_expression(&in_expr.expressions[0])?;
17738                }
17739            } else {
17740                let is_bare_ref = single_expr && matches!(
17741                    &in_expr.expressions[0],
17742                    Expression::Column(_) | Expression::Identifier(_) | Expression::Dot(_)
17743                );
17744                if is_duckdb && is_bare_ref {
17745                    self.write_space();
17746                    self.generate_expression(&in_expr.expressions[0])?;
17747                } else {
17748                    // Standard IN (list)
17749                    self.write(" (");
17750                    for (i, expr) in in_expr.expressions.iter().enumerate() {
17751                        if i > 0 {
17752                            self.write(", ");
17753                        }
17754                        self.generate_expression(expr)?;
17755                    }
17756                    self.write(")");
17757                }
17758            }
17759        }
17760
17761        Ok(())
17762    }
17763
17764    fn generate_between(&mut self, between: &Between) -> Result<()> {
17765        self.generate_expression(&between.this)?;
17766        if between.not {
17767            self.write_space();
17768            self.write_keyword("NOT");
17769        }
17770        self.write_space();
17771        self.write_keyword("BETWEEN");
17772        self.write_space();
17773        self.generate_expression(&between.low)?;
17774        self.write_space();
17775        self.write_keyword("AND");
17776        self.write_space();
17777        self.generate_expression(&between.high)
17778    }
17779
17780    fn generate_is_null(&mut self, is_null: &IsNull) -> Result<()> {
17781        // Python sqlglot normalizes NOTNULL postfix to NOT x IS NULL
17782        if is_null.not && is_null.postfix_form {
17783            // NOTNULL → NOT x IS NULL
17784            self.write_keyword("NOT");
17785            self.write_space();
17786            self.generate_expression(&is_null.this)?;
17787            self.write_space();
17788            self.write_keyword("IS");
17789            self.write_space();
17790            self.write_keyword("NULL");
17791        } else {
17792            self.generate_expression(&is_null.this)?;
17793            self.write_space();
17794            self.write_keyword("IS");
17795            if is_null.not {
17796                self.write_space();
17797                self.write_keyword("NOT");
17798            }
17799            self.write_space();
17800            self.write_keyword("NULL");
17801        }
17802        Ok(())
17803    }
17804
17805    fn generate_is_true(&mut self, is_true: &IsTrueFalse) -> Result<()> {
17806        self.generate_expression(&is_true.this)?;
17807        self.write_space();
17808        self.write_keyword("IS");
17809        if is_true.not {
17810            self.write_space();
17811            self.write_keyword("NOT");
17812        }
17813        self.write_space();
17814        self.write_keyword("TRUE");
17815        Ok(())
17816    }
17817
17818    fn generate_is_false(&mut self, is_false: &IsTrueFalse) -> Result<()> {
17819        self.generate_expression(&is_false.this)?;
17820        self.write_space();
17821        self.write_keyword("IS");
17822        if is_false.not {
17823            self.write_space();
17824            self.write_keyword("NOT");
17825        }
17826        self.write_space();
17827        self.write_keyword("FALSE");
17828        Ok(())
17829    }
17830
17831    fn generate_is_json(&mut self, is_json: &IsJson) -> Result<()> {
17832        self.generate_expression(&is_json.this)?;
17833        self.write_space();
17834        self.write_keyword("IS");
17835        if is_json.negated {
17836            self.write_space();
17837            self.write_keyword("NOT");
17838        }
17839        self.write_space();
17840        self.write_keyword("JSON");
17841
17842        // Output JSON type if specified (VALUE, SCALAR, OBJECT, ARRAY)
17843        if let Some(ref json_type) = is_json.json_type {
17844            self.write_space();
17845            self.write_keyword(json_type);
17846        }
17847
17848        // Output key uniqueness constraint if specified
17849        match &is_json.unique_keys {
17850            Some(JsonUniqueKeys::With) => {
17851                self.write_space();
17852                self.write_keyword("WITH UNIQUE KEYS");
17853            }
17854            Some(JsonUniqueKeys::Without) => {
17855                self.write_space();
17856                self.write_keyword("WITHOUT UNIQUE KEYS");
17857            }
17858            Some(JsonUniqueKeys::Shorthand) => {
17859                self.write_space();
17860                self.write_keyword("UNIQUE KEYS");
17861            }
17862            None => {}
17863        }
17864
17865        Ok(())
17866    }
17867
17868    fn generate_is(&mut self, is_expr: &BinaryOp) -> Result<()> {
17869        self.generate_expression(&is_expr.left)?;
17870        self.write_space();
17871        self.write_keyword("IS");
17872        self.write_space();
17873        self.generate_expression(&is_expr.right)
17874    }
17875
17876    fn generate_exists(&mut self, exists: &Exists) -> Result<()> {
17877        if exists.not {
17878            self.write_keyword("NOT");
17879            self.write_space();
17880        }
17881        self.write_keyword("EXISTS");
17882        self.write("(");
17883        self.generate_expression(&exists.this)?;
17884        self.write(")");
17885        Ok(())
17886    }
17887
17888    fn generate_member_of(&mut self, op: &BinaryOp) -> Result<()> {
17889        self.generate_expression(&op.left)?;
17890        self.write_space();
17891        self.write_keyword("MEMBER OF");
17892        self.write("(");
17893        self.generate_expression(&op.right)?;
17894        self.write(")");
17895        Ok(())
17896    }
17897
17898    fn generate_subquery(&mut self, subquery: &Subquery) -> Result<()> {
17899        if subquery.lateral {
17900            self.write_keyword("LATERAL");
17901            self.write_space();
17902        }
17903
17904        // If the inner expression is a Paren, don't add extra parentheses
17905        // This handles cases like ((SELECT 1)) LIMIT 1 where we wrap Paren in Subquery
17906        // to carry the LIMIT modifier without adding more parens
17907        let skip_outer_parens = matches!(&subquery.this, Expression::Paren(_));
17908
17909        // Check if inner expression is a statement for pretty formatting
17910        let is_statement = matches!(
17911            &subquery.this,
17912            Expression::Select(_) | Expression::Union(_) |
17913            Expression::Intersect(_) | Expression::Except(_) |
17914            Expression::Merge(_)
17915        );
17916
17917        if !skip_outer_parens {
17918            self.write("(");
17919            if self.config.pretty && is_statement {
17920                self.write_newline();
17921                self.indent_level += 1;
17922                self.write_indent();
17923            }
17924        }
17925        self.generate_expression(&subquery.this)?;
17926
17927        // Generate ORDER BY, LIMIT, OFFSET based on modifiers_inside flag
17928        if subquery.modifiers_inside {
17929            // Generate modifiers INSIDE the parentheses: (SELECT ... LIMIT 1)
17930            if let Some(order_by) = &subquery.order_by {
17931                self.write_space();
17932                self.write_keyword("ORDER BY");
17933                self.write_space();
17934                for (i, ord) in order_by.expressions.iter().enumerate() {
17935                    if i > 0 {
17936                        self.write(", ");
17937                    }
17938                    self.generate_ordered(ord)?;
17939                }
17940            }
17941
17942            if let Some(limit) = &subquery.limit {
17943                self.write_space();
17944                self.write_keyword("LIMIT");
17945                self.write_space();
17946                self.generate_expression(&limit.this)?;
17947                if limit.percent {
17948                    self.write_space();
17949                    self.write_keyword("PERCENT");
17950                }
17951            }
17952
17953            if let Some(offset) = &subquery.offset {
17954                self.write_space();
17955                self.write_keyword("OFFSET");
17956                self.write_space();
17957                self.generate_expression(&offset.this)?;
17958            }
17959        }
17960
17961        if !skip_outer_parens {
17962            if self.config.pretty && is_statement {
17963                self.write_newline();
17964                self.indent_level -= 1;
17965                self.write_indent();
17966            }
17967            self.write(")");
17968        }
17969
17970        // Generate modifiers OUTSIDE the parentheses: (SELECT ...) LIMIT 1
17971        if !subquery.modifiers_inside {
17972            if let Some(order_by) = &subquery.order_by {
17973                self.write_space();
17974                self.write_keyword("ORDER BY");
17975                self.write_space();
17976                for (i, ord) in order_by.expressions.iter().enumerate() {
17977                    if i > 0 {
17978                        self.write(", ");
17979                    }
17980                    self.generate_ordered(ord)?;
17981                }
17982            }
17983
17984            if let Some(limit) = &subquery.limit {
17985                self.write_space();
17986                self.write_keyword("LIMIT");
17987                self.write_space();
17988                self.generate_expression(&limit.this)?;
17989                if limit.percent {
17990                    self.write_space();
17991                    self.write_keyword("PERCENT");
17992                }
17993            }
17994
17995            if let Some(offset) = &subquery.offset {
17996                self.write_space();
17997                self.write_keyword("OFFSET");
17998                self.write_space();
17999                self.generate_expression(&offset.this)?;
18000            }
18001
18002            // Generate DISTRIBUTE BY (Hive/Spark)
18003            if let Some(distribute_by) = &subquery.distribute_by {
18004                self.write_space();
18005                self.write_keyword("DISTRIBUTE BY");
18006                self.write_space();
18007                for (i, expr) in distribute_by.expressions.iter().enumerate() {
18008                    if i > 0 {
18009                        self.write(", ");
18010                    }
18011                    self.generate_expression(expr)?;
18012                }
18013            }
18014
18015            // Generate SORT BY (Hive/Spark)
18016            if let Some(sort_by) = &subquery.sort_by {
18017                self.write_space();
18018                self.write_keyword("SORT BY");
18019                self.write_space();
18020                for (i, ord) in sort_by.expressions.iter().enumerate() {
18021                    if i > 0 {
18022                        self.write(", ");
18023                    }
18024                    self.generate_ordered(ord)?;
18025                }
18026            }
18027
18028            // Generate CLUSTER BY (Hive/Spark)
18029            if let Some(cluster_by) = &subquery.cluster_by {
18030                self.write_space();
18031                self.write_keyword("CLUSTER BY");
18032                self.write_space();
18033                for (i, ord) in cluster_by.expressions.iter().enumerate() {
18034                    if i > 0 {
18035                        self.write(", ");
18036                    }
18037                    self.generate_ordered(ord)?;
18038                }
18039            }
18040        }
18041
18042        if let Some(alias) = &subquery.alias {
18043            self.write_space();
18044            // Oracle doesn't use AS for subquery aliases
18045            let skip_as = matches!(self.config.dialect, Some(crate::dialects::DialectType::Oracle));
18046            if !skip_as {
18047                self.write_keyword("AS");
18048                self.write_space();
18049            }
18050            self.generate_identifier(alias)?;
18051            if !subquery.column_aliases.is_empty() {
18052                self.write("(");
18053                for (i, col) in subquery.column_aliases.iter().enumerate() {
18054                    if i > 0 {
18055                        self.write(", ");
18056                    }
18057                    self.generate_identifier(col)?;
18058                }
18059                self.write(")");
18060            }
18061        }
18062        // Output trailing comments
18063        for comment in &subquery.trailing_comments {
18064            self.write(" ");
18065            self.write(comment);
18066        }
18067        Ok(())
18068    }
18069
18070    fn generate_pivot(&mut self, pivot: &Pivot) -> Result<()> {
18071        // Generate WITH clause if present
18072        if let Some(ref with) = pivot.with {
18073            self.generate_with(with)?;
18074            self.write_space();
18075        }
18076
18077        let direction = if pivot.unpivot { "UNPIVOT" } else { "PIVOT" };
18078
18079        // Check for Redshift UNPIVOT in FROM clause:
18080        // UNPIVOT expr [AS val AT attr]
18081        // This is when unpivot=true, expressions is empty, fields is empty, and this is not Null
18082        let is_redshift_unpivot = pivot.unpivot
18083            && pivot.expressions.is_empty()
18084            && pivot.fields.is_empty()
18085            && pivot.using.is_empty()
18086            && pivot.into.is_none()
18087            && !matches!(&pivot.this, Expression::Null(_));
18088
18089        if is_redshift_unpivot {
18090            // Redshift UNPIVOT: UNPIVOT expr [AS alias]
18091            self.write_keyword("UNPIVOT");
18092            self.write_space();
18093            self.generate_expression(&pivot.this)?;
18094            // Alias - for Redshift it can be "val AT attr" format
18095            if let Some(alias) = &pivot.alias {
18096                self.write_space();
18097                self.write_keyword("AS");
18098                self.write_space();
18099                // The alias might contain " AT " for the attr part
18100                self.write(&alias.name);
18101            }
18102            return Ok(());
18103        }
18104
18105        // Check if this is a DuckDB simplified pivot (has `using` or `into`, or no `fields`)
18106        let is_simplified = !pivot.using.is_empty() || pivot.into.is_some()
18107            || (pivot.fields.is_empty() && !pivot.expressions.is_empty()
18108                && !matches!(&pivot.this, Expression::Null(_)));
18109
18110        if is_simplified {
18111            // DuckDB simplified syntax:
18112            //   PIVOT table ON cols [IN (...)] USING agg [AS alias], ... [GROUP BY ...]
18113            //   UNPIVOT table ON cols INTO NAME col VALUE col
18114            self.write_keyword(direction);
18115            self.write_space();
18116            self.generate_expression(&pivot.this)?;
18117
18118            if !pivot.expressions.is_empty() {
18119                self.write_space();
18120                self.write_keyword("ON");
18121                self.write_space();
18122                for (i, expr) in pivot.expressions.iter().enumerate() {
18123                    if i > 0 {
18124                        self.write(", ");
18125                    }
18126                    self.generate_expression(expr)?;
18127                }
18128            }
18129
18130            // INTO (for UNPIVOT)
18131            if let Some(into) = &pivot.into {
18132                self.write_space();
18133                self.write_keyword("INTO");
18134                self.write_space();
18135                self.generate_expression(into)?;
18136            }
18137
18138            // USING (for PIVOT)
18139            if !pivot.using.is_empty() {
18140                self.write_space();
18141                self.write_keyword("USING");
18142                self.write_space();
18143                for (i, expr) in pivot.using.iter().enumerate() {
18144                    if i > 0 {
18145                        self.write(", ");
18146                    }
18147                    self.generate_expression(expr)?;
18148                }
18149            }
18150
18151            // GROUP BY
18152            if let Some(group) = &pivot.group {
18153                self.write_space();
18154                self.generate_expression(group)?;
18155            }
18156        } else {
18157            // Standard syntax:
18158            //   table PIVOT(agg [AS alias], ... FOR col IN (val [AS alias], ...) [GROUP BY ...])
18159            //   table UNPIVOT(value_col FOR name_col IN (col1, col2, ...))
18160            // Only output the table expression if it's not a Null (null is used when PIVOT comes after JOIN ON)
18161            if !matches!(&pivot.this, Expression::Null(_)) {
18162                self.generate_expression(&pivot.this)?;
18163                self.write_space();
18164            }
18165            self.write_keyword(direction);
18166            self.write("(");
18167
18168            // Aggregation expressions
18169            for (i, expr) in pivot.expressions.iter().enumerate() {
18170                if i > 0 {
18171                    self.write(", ");
18172                }
18173                self.generate_expression(expr)?;
18174            }
18175
18176            // FOR...IN fields
18177            if !pivot.fields.is_empty() {
18178                if !pivot.expressions.is_empty() {
18179                    self.write_space();
18180                }
18181                self.write_keyword("FOR");
18182                self.write_space();
18183                for (i, field) in pivot.fields.iter().enumerate() {
18184                    if i > 0 {
18185                        self.write_space();
18186                    }
18187                    // field is an In expression: column IN (values)
18188                    self.generate_expression(field)?;
18189                }
18190            }
18191
18192            // DEFAULT ON NULL
18193            if let Some(default_val) = &pivot.default_on_null {
18194                self.write_space();
18195                self.write_keyword("DEFAULT ON NULL");
18196                self.write(" (");
18197                self.generate_expression(default_val)?;
18198                self.write(")");
18199            }
18200
18201            // GROUP BY inside PIVOT parens
18202            if let Some(group) = &pivot.group {
18203                self.write_space();
18204                self.generate_expression(group)?;
18205            }
18206
18207            self.write(")");
18208        }
18209
18210        // Alias
18211        if let Some(alias) = &pivot.alias {
18212            self.write_space();
18213            self.write_keyword("AS");
18214            self.write_space();
18215            self.generate_identifier(alias)?;
18216        }
18217
18218        Ok(())
18219    }
18220
18221    fn generate_unpivot(&mut self, unpivot: &Unpivot) -> Result<()> {
18222        self.generate_expression(&unpivot.this)?;
18223        self.write_space();
18224        self.write_keyword("UNPIVOT");
18225        // Output INCLUDE NULLS or EXCLUDE NULLS if specified
18226        if let Some(include) = unpivot.include_nulls {
18227            self.write_space();
18228            if include {
18229                self.write_keyword("INCLUDE NULLS");
18230            } else {
18231                self.write_keyword("EXCLUDE NULLS");
18232            }
18233            self.write_space();
18234        }
18235        self.write("(");
18236        if unpivot.value_column_parenthesized {
18237            self.write("(");
18238        }
18239        self.generate_identifier(&unpivot.value_column)?;
18240        // Output additional value columns if present
18241        for extra_col in &unpivot.extra_value_columns {
18242            self.write(", ");
18243            self.generate_identifier(extra_col)?;
18244        }
18245        if unpivot.value_column_parenthesized {
18246            self.write(")");
18247        }
18248        self.write_space();
18249        self.write_keyword("FOR");
18250        self.write_space();
18251        self.generate_identifier(&unpivot.name_column)?;
18252        self.write_space();
18253        self.write_keyword("IN");
18254        self.write(" (");
18255        for (i, col) in unpivot.columns.iter().enumerate() {
18256            if i > 0 {
18257                self.write(", ");
18258            }
18259            self.generate_expression(col)?;
18260        }
18261        self.write("))");
18262        if let Some(alias) = &unpivot.alias {
18263            self.write_space();
18264            self.write_keyword("AS");
18265            self.write_space();
18266            self.generate_identifier(alias)?;
18267        }
18268        Ok(())
18269    }
18270
18271    fn generate_values(&mut self, values: &Values) -> Result<()> {
18272        self.write_keyword("VALUES");
18273        for (i, row) in values.expressions.iter().enumerate() {
18274            if i > 0 {
18275                self.write(",");
18276            }
18277            self.write(" (");
18278            for (j, expr) in row.expressions.iter().enumerate() {
18279                if j > 0 {
18280                    self.write(", ");
18281                }
18282                self.generate_expression(expr)?;
18283            }
18284            self.write(")");
18285        }
18286        if let Some(alias) = &values.alias {
18287            self.write_space();
18288            self.write_keyword("AS");
18289            self.write_space();
18290            self.generate_identifier(alias)?;
18291            if !values.column_aliases.is_empty() {
18292                self.write("(");
18293                for (i, col) in values.column_aliases.iter().enumerate() {
18294                    if i > 0 {
18295                        self.write(", ");
18296                    }
18297                    self.generate_identifier(col)?;
18298                }
18299                self.write(")");
18300            }
18301        }
18302        Ok(())
18303    }
18304
18305    fn generate_array(&mut self, arr: &Array) -> Result<()> {
18306        // Apply struct name inheritance for target dialects that need it
18307        let needs_inheritance = matches!(self.config.dialect,
18308            Some(DialectType::DuckDB) | Some(DialectType::Spark)
18309            | Some(DialectType::Databricks) | Some(DialectType::Hive)
18310            | Some(DialectType::Snowflake) | Some(DialectType::Presto) | Some(DialectType::Trino)
18311        );
18312        let propagated: Vec<Expression>;
18313        let expressions = if needs_inheritance && arr.expressions.len() > 1 {
18314            propagated = Self::inherit_struct_field_names(&arr.expressions);
18315            &propagated
18316        } else {
18317            &arr.expressions
18318        };
18319
18320        if !self.config.array_bracket_only {
18321            self.write_keyword("ARRAY");
18322        }
18323        self.write("[");
18324        for (i, expr) in expressions.iter().enumerate() {
18325            if i > 0 {
18326                self.write(", ");
18327            }
18328            self.generate_expression(expr)?;
18329        }
18330        self.write("]");
18331        Ok(())
18332    }
18333
18334    fn generate_tuple(&mut self, tuple: &Tuple) -> Result<()> {
18335        // Special case: Tuple(function/expr, TableAlias) pattern for table functions with typed aliases
18336        // Used for PostgreSQL functions like JSON_TO_RECORDSET: FUNC(args) AS alias(col1 type1, col2 type2)
18337        if tuple.expressions.len() == 2 {
18338            if let Expression::TableAlias(_) = &tuple.expressions[1] {
18339                // First element is the function/expression, second is the TableAlias
18340                self.generate_expression(&tuple.expressions[0])?;
18341                self.write_space();
18342                self.write_keyword("AS");
18343                self.write_space();
18344                self.generate_expression(&tuple.expressions[1])?;
18345                return Ok(());
18346            }
18347        }
18348
18349        // In pretty mode, format long tuples with each element on a new line
18350        if self.config.pretty && tuple.expressions.len() > 1 {
18351            self.write("(");
18352            self.write_newline();
18353            self.indent_level += 1;
18354            for (i, expr) in tuple.expressions.iter().enumerate() {
18355                if i > 0 {
18356                    self.write(",");
18357                    self.write_newline();
18358                }
18359                self.write_indent();
18360                self.generate_expression(expr)?;
18361            }
18362            self.indent_level -= 1;
18363            self.write_newline();
18364            self.write(")");
18365        } else {
18366            self.write("(");
18367            for (i, expr) in tuple.expressions.iter().enumerate() {
18368                if i > 0 {
18369                    self.write(", ");
18370                }
18371                self.generate_expression(expr)?;
18372            }
18373            self.write(")");
18374        }
18375        Ok(())
18376    }
18377
18378    fn generate_ordered(&mut self, ordered: &Ordered) -> Result<()> {
18379        self.generate_expression(&ordered.this)?;
18380        if ordered.desc {
18381            self.write_space();
18382            self.write_keyword("DESC");
18383        } else if ordered.explicit_asc {
18384            self.write_space();
18385            self.write_keyword("ASC");
18386        }
18387        if let Some(nulls_first) = ordered.nulls_first {
18388            // Determine if we should skip outputting NULLS FIRST/LAST when it's the default
18389            // for the dialect. Different dialects have different NULL ordering defaults:
18390            //
18391            // nulls_are_large (Oracle, Postgres, Snowflake, etc.):
18392            //   - ASC: NULLS LAST is default (omit NULLS LAST for ASC)
18393            //   - DESC: NULLS FIRST is default (omit NULLS FIRST for DESC)
18394            //
18395            // nulls_are_small (Spark, Hive, BigQuery, most others):
18396            //   - ASC: NULLS FIRST is default
18397            //   - DESC: NULLS LAST is default
18398            //
18399            // nulls_are_last (DuckDB, Presto, Trino, Dremio, etc.):
18400            //   - NULLS LAST is always the default regardless of sort direction
18401            let is_asc = !ordered.desc;
18402            let is_nulls_are_large = matches!(
18403                self.config.dialect,
18404                Some(DialectType::Oracle) | Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)
18405                | Some(DialectType::Snowflake)
18406            );
18407            let is_nulls_are_last = matches!(
18408                self.config.dialect,
18409                Some(DialectType::Dremio) | Some(DialectType::DuckDB) | Some(DialectType::Presto)
18410                | Some(DialectType::Trino) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
18411                | Some(DialectType::Drill) | Some(DialectType::Exasol)
18412            );
18413
18414            // Check if the NULLS ordering matches the default for this dialect
18415            let is_default_nulls = if is_nulls_are_large {
18416                // For nulls_are_large: ASC + NULLS LAST or DESC + NULLS FIRST is default
18417                (is_asc && !nulls_first) || (!is_asc && nulls_first)
18418            } else if is_nulls_are_last {
18419                // For nulls_are_last: NULLS LAST is always default
18420                !nulls_first
18421            } else {
18422                false
18423            };
18424
18425            if !is_default_nulls {
18426                self.write_space();
18427                self.write_keyword("NULLS");
18428                self.write_space();
18429                self.write_keyword(if nulls_first { "FIRST" } else { "LAST" });
18430            }
18431        }
18432        // WITH FILL clause (ClickHouse)
18433        if let Some(ref with_fill) = ordered.with_fill {
18434            self.write_space();
18435            self.generate_with_fill(with_fill)?;
18436        }
18437        Ok(())
18438    }
18439
18440    fn generate_data_type(&mut self, dt: &DataType) -> Result<()> {
18441        use crate::dialects::DialectType;
18442
18443        match dt {
18444            DataType::Boolean => {
18445                // Dialect-specific boolean type mappings
18446                match self.config.dialect {
18447                    Some(DialectType::TSQL) => self.write_keyword("BIT"),
18448                    Some(DialectType::MySQL) => self.write_keyword("BOOLEAN"), // alias for TINYINT(1)
18449                    Some(DialectType::Oracle) => {
18450                        // Oracle 23c+ supports BOOLEAN, older versions use NUMBER(1)
18451                        self.write_keyword("NUMBER(1)")
18452                    }
18453                    Some(DialectType::ClickHouse) => self.write("Bool"), // ClickHouse uses Bool (case-sensitive)
18454                    _ => self.write_keyword("BOOLEAN"),
18455                }
18456            }
18457            DataType::TinyInt { length } => {
18458                // PostgreSQL, Oracle, and Exasol don't have TINYINT, use SMALLINT
18459                // Dremio maps TINYINT to INT
18460                match self.config.dialect {
18461                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) | Some(DialectType::Oracle) | Some(DialectType::Exasol) => {
18462                        self.write_keyword("SMALLINT");
18463                    }
18464                    Some(DialectType::Teradata) => {
18465                        // Teradata uses BYTEINT for smallest integer
18466                        self.write_keyword("BYTEINT");
18467                    }
18468                    Some(DialectType::Dremio) => {
18469                        // Dremio maps TINYINT to INT
18470                        self.write_keyword("INT");
18471                    }
18472                    _ => {
18473                        self.write_keyword("TINYINT");
18474                    }
18475                }
18476                if let Some(n) = length {
18477                    if !matches!(self.config.dialect, Some(DialectType::Dremio)) {
18478                        self.write(&format!("({})", n));
18479                    }
18480                }
18481            }
18482            DataType::SmallInt { length } => {
18483                // Dremio maps SMALLINT to INT, SQLite maps SMALLINT to INTEGER
18484                match self.config.dialect {
18485                    Some(DialectType::Dremio) => {
18486                        self.write_keyword("INT");
18487                    }
18488                    Some(DialectType::SQLite) => {
18489                        self.write_keyword("INTEGER");
18490                    }
18491                    _ => {
18492                        self.write_keyword("SMALLINT");
18493                        if let Some(n) = length {
18494                            self.write(&format!("({})", n));
18495                        }
18496                    }
18497                }
18498            }
18499            DataType::Int { length, integer_spelling } => {
18500                // BigQuery uses INT64 for INT
18501                if matches!(self.config.dialect, Some(DialectType::BigQuery)) {
18502                    self.write_keyword("INT64");
18503                } else if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
18504                    self.write("Int32");
18505                } else {
18506                    // TSQL, Presto, Trino, SQLite, Redshift use INTEGER as the canonical form
18507                    let use_integer = match self.config.dialect {
18508                        Some(DialectType::TSQL) | Some(DialectType::Fabric)
18509                        | Some(DialectType::Presto) | Some(DialectType::Trino)
18510                        | Some(DialectType::SQLite) | Some(DialectType::Redshift) => true,
18511                        // Databricks preserves the original spelling
18512                        Some(DialectType::Databricks) => *integer_spelling,
18513                        _ => false,
18514                    };
18515                    if use_integer {
18516                        self.write_keyword("INTEGER");
18517                    } else {
18518                        self.write_keyword("INT");
18519                    }
18520                    if let Some(n) = length {
18521                        self.write(&format!("({})", n));
18522                    }
18523                }
18524            }
18525            DataType::BigInt { length } => {
18526                // Dialect-specific bigint type mappings
18527                match self.config.dialect {
18528                    Some(DialectType::Oracle) => {
18529                        // Oracle doesn't have BIGINT, uses NUMBER or INT
18530                        self.write_keyword("NUMBER(19)");
18531                    }
18532                    _ => {
18533                        self.write_keyword("BIGINT");
18534                        if let Some(n) = length {
18535                            self.write(&format!("({})", n));
18536                        }
18537                    }
18538                }
18539            }
18540            DataType::Float { precision, scale, real_spelling } => {
18541                // Dialect-specific float type mappings
18542                // If real_spelling is true, preserve REAL; otherwise use dialect default
18543                // Spark/Hive don't support REAL, always use FLOAT
18544                if *real_spelling && !matches!(self.config.dialect, Some(DialectType::Spark)
18545                    | Some(DialectType::Databricks) | Some(DialectType::Hive)
18546                    | Some(DialectType::Snowflake) | Some(DialectType::MySQL)
18547                    | Some(DialectType::BigQuery)) {
18548                    self.write_keyword("REAL")
18549                } else {
18550                    match self.config.dialect {
18551                        Some(DialectType::PostgreSQL) => {
18552                            self.write_keyword("REAL")
18553                        }
18554                        Some(DialectType::BigQuery) => {
18555                            self.write_keyword("FLOAT64")
18556                        }
18557                        _ => self.write_keyword("FLOAT"),
18558                    }
18559                }
18560                // MySQL supports FLOAT(precision) or FLOAT(precision, scale)
18561                // Spark/Hive don't support FLOAT(precision)
18562                if !matches!(self.config.dialect, Some(DialectType::Spark)
18563                    | Some(DialectType::Databricks) | Some(DialectType::Hive)
18564                    | Some(DialectType::Presto) | Some(DialectType::Trino)) {
18565                    if let Some(p) = precision {
18566                        self.write(&format!("({}", p));
18567                        if let Some(s) = scale {
18568                            self.write(&format!(", {})", s));
18569                        } else {
18570                            self.write(")");
18571                        }
18572                    }
18573                }
18574            }
18575            DataType::Double { precision, scale } => {
18576                // Dialect-specific double type mappings
18577                match self.config.dialect {
18578                    Some(DialectType::TSQL) | Some(DialectType::Fabric) => self.write_keyword("FLOAT"), // SQL Server/Fabric FLOAT is double
18579                    Some(DialectType::Oracle) => self.write_keyword("DOUBLE PRECISION"),
18580                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) | Some(DialectType::Teradata) => {
18581                        self.write_keyword("DOUBLE PRECISION")
18582                    }
18583                    _ => self.write_keyword("DOUBLE"),
18584                }
18585                // MySQL supports DOUBLE(precision, scale)
18586                if let Some(p) = precision {
18587                    self.write(&format!("({}", p));
18588                    if let Some(s) = scale {
18589                        self.write(&format!(", {})", s));
18590                    } else {
18591                        self.write(")");
18592                    }
18593                }
18594            }
18595            DataType::Decimal { precision, scale } => {
18596                // Dialect-specific decimal type mappings
18597                match self.config.dialect {
18598                    Some(DialectType::ClickHouse) => {
18599                        self.write("Decimal");
18600                        if let Some(p) = precision {
18601                            self.write(&format!("({}", p));
18602                            if let Some(s) = scale {
18603                                self.write(&format!(", {}", s));
18604                            }
18605                            self.write(")");
18606                        }
18607                    }
18608                    Some(DialectType::Oracle) => {
18609                        // Oracle uses NUMBER instead of DECIMAL
18610                        self.write_keyword("NUMBER");
18611                        if let Some(p) = precision {
18612                            self.write(&format!("({}", p));
18613                            if let Some(s) = scale {
18614                                self.write(&format!(", {}", s));
18615                            }
18616                            self.write(")");
18617                        }
18618                    }
18619                    Some(DialectType::BigQuery) => {
18620                        // BigQuery uses NUMERIC instead of DECIMAL
18621                        self.write_keyword("NUMERIC");
18622                        if let Some(p) = precision {
18623                            self.write(&format!("({}", p));
18624                            if let Some(s) = scale {
18625                                self.write(&format!(", {}", s));
18626                            }
18627                            self.write(")");
18628                        }
18629                    }
18630                    _ => {
18631                        self.write_keyword("DECIMAL");
18632                        if let Some(p) = precision {
18633                            self.write(&format!("({}", p));
18634                            if let Some(s) = scale {
18635                                self.write(&format!(", {}", s));
18636                            }
18637                            self.write(")");
18638                        }
18639                    }
18640                }
18641            }
18642            DataType::Char { length } => {
18643                // Dialect-specific char type mappings
18644                match self.config.dialect {
18645                    Some(DialectType::DuckDB) => {
18646                        // DuckDB maps CHAR to TEXT
18647                        self.write_keyword("TEXT");
18648                    }
18649                    Some(DialectType::Dremio) => {
18650                        // Dremio maps CHAR to VARCHAR
18651                        self.write_keyword("VARCHAR");
18652                        if let Some(n) = length {
18653                            self.write(&format!("({})", n));
18654                        }
18655                    }
18656                    _ => {
18657                        self.write_keyword("CHAR");
18658                        if let Some(n) = length {
18659                            self.write(&format!("({})", n));
18660                        }
18661                    }
18662                }
18663            }
18664            DataType::VarChar { length, parenthesized_length } => {
18665                // Dialect-specific varchar type mappings
18666                match self.config.dialect {
18667                    Some(DialectType::Oracle) => {
18668                        self.write_keyword("VARCHAR2");
18669                        if let Some(n) = length {
18670                            self.write(&format!("({})", n));
18671                        }
18672                    }
18673                    Some(DialectType::DuckDB) => {
18674                        // DuckDB maps VARCHAR to TEXT
18675                        self.write_keyword("TEXT");
18676                    }
18677                    Some(DialectType::SQLite) => {
18678                        // SQLite maps VARCHAR to TEXT, preserving length
18679                        self.write_keyword("TEXT");
18680                        if let Some(n) = length {
18681                            self.write(&format!("({})", n));
18682                        }
18683                    }
18684                    Some(DialectType::MySQL) if length.is_none() => {
18685                        // MySQL requires VARCHAR to have a size - if it doesn't, use TEXT
18686                        self.write_keyword("TEXT");
18687                    }
18688                    Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks) if length.is_none() => {
18689                        // Hive/Spark/Databricks: VARCHAR without length → STRING
18690                        self.write_keyword("STRING");
18691                    }
18692                    _ => {
18693                        self.write_keyword("VARCHAR");
18694                        if let Some(n) = length {
18695                            // Hive uses VARCHAR((n)) with extra parentheses in STRUCT definitions
18696                            if *parenthesized_length {
18697                                self.write(&format!("(({}))", n));
18698                            } else {
18699                                self.write(&format!("({})", n));
18700                            }
18701                        }
18702                    }
18703                }
18704            }
18705            DataType::Text => {
18706                // Dialect-specific text type mappings
18707                match self.config.dialect {
18708                    Some(DialectType::Oracle) => self.write_keyword("CLOB"),
18709                    Some(DialectType::TSQL) => self.write_keyword("NVARCHAR(MAX)"),
18710                    Some(DialectType::BigQuery) => self.write_keyword("STRING"),
18711                    Some(DialectType::Snowflake) | Some(DialectType::Dremio) => self.write_keyword("VARCHAR"),
18712                    Some(DialectType::Exasol) => self.write_keyword("LONG VARCHAR"),
18713                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARCHAR"),
18714                    Some(DialectType::Spark) | Some(DialectType::Databricks)
18715                    | Some(DialectType::Hive) => self.write_keyword("STRING"),
18716                    Some(DialectType::Redshift) => self.write_keyword("VARCHAR"),
18717                    Some(DialectType::StarRocks) => self.write_keyword("STRING"),
18718                    _ => self.write_keyword("TEXT"),
18719                }
18720            }
18721            DataType::String { length } => {
18722                // STRING type with optional length (BigQuery STRING(n))
18723                match self.config.dialect {
18724                    Some(DialectType::ClickHouse) => {
18725                        // ClickHouse uses String with specific casing
18726                        self.write("String");
18727                        if let Some(n) = length {
18728                            self.write(&format!("({})", n));
18729                        }
18730                    }
18731                    Some(DialectType::BigQuery) => {
18732                        self.write_keyword("STRING");
18733                        if let Some(n) = length {
18734                            self.write(&format!("({})", n));
18735                        }
18736                    }
18737                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
18738                        // PostgreSQL doesn't have STRING - use VARCHAR or TEXT
18739                        if let Some(n) = length {
18740                            self.write_keyword("VARCHAR");
18741                            self.write(&format!("({})", n));
18742                        } else {
18743                            self.write_keyword("TEXT");
18744                        }
18745                    }
18746                    Some(DialectType::MySQL) | Some(DialectType::StarRocks) | Some(DialectType::Doris) => {
18747                        // MySQL doesn't have STRING - use VARCHAR or TEXT
18748                        if let Some(n) = length {
18749                            self.write_keyword("VARCHAR");
18750                            self.write(&format!("({})", n));
18751                        } else {
18752                            self.write_keyword("TEXT");
18753                        }
18754                    }
18755                    Some(DialectType::TSQL) => {
18756                        // TSQL doesn't have STRING - use VARCHAR or NVARCHAR(MAX)
18757                        if let Some(n) = length {
18758                            self.write_keyword("VARCHAR");
18759                            self.write(&format!("({})", n));
18760                        } else {
18761                            self.write_keyword("NVARCHAR(MAX)");
18762                        }
18763                    }
18764                    Some(DialectType::DuckDB) => {
18765                        // DuckDB uses TEXT for string types
18766                        self.write_keyword("TEXT");
18767                        if let Some(n) = length {
18768                            self.write(&format!("({})", n));
18769                        }
18770                    }
18771                    Some(DialectType::Presto) | Some(DialectType::Trino) => {
18772                        // Presto/Trino use VARCHAR for string types
18773                        self.write_keyword("VARCHAR");
18774                        if let Some(n) = length {
18775                            self.write(&format!("({})", n));
18776                        }
18777                    }
18778                    _ => {
18779                        // Default: output STRING with optional length
18780                        self.write_keyword("STRING");
18781                        if let Some(n) = length {
18782                            self.write(&format!("({})", n));
18783                        }
18784                    }
18785                }
18786            }
18787            DataType::Binary { length } => {
18788                // Dialect-specific binary type mappings
18789                match self.config.dialect {
18790                    Some(DialectType::PostgreSQL) => {
18791                        self.write_keyword("BYTEA");
18792                    }
18793                    Some(DialectType::Redshift) => {
18794                        self.write_keyword("VARBYTE");
18795                    }
18796                    Some(DialectType::DuckDB) => {
18797                        // DuckDB maps BINARY to BLOB
18798                        self.write_keyword("BLOB");
18799                    }
18800                    Some(DialectType::Dremio) => {
18801                        // Dremio maps BINARY to VARBINARY
18802                        self.write_keyword("VARBINARY");
18803                        if let Some(n) = length {
18804                            self.write(&format!("({})", n));
18805                        }
18806                    }
18807                    _ => {
18808                        self.write_keyword("BINARY");
18809                        if let Some(n) = length {
18810                            self.write(&format!("({})", n));
18811                        }
18812                    }
18813                }
18814            }
18815            DataType::VarBinary { length } => {
18816                // Dialect-specific varbinary type mappings
18817                match self.config.dialect {
18818                    Some(DialectType::PostgreSQL) => {
18819                        self.write_keyword("BYTEA");
18820                    }
18821                    Some(DialectType::Redshift) => {
18822                        self.write_keyword("VARBYTE");
18823                        if let Some(n) = length {
18824                            self.write(&format!("({})", n));
18825                        }
18826                    }
18827                    Some(DialectType::DuckDB) => {
18828                        // DuckDB maps VARBINARY to BLOB
18829                        self.write_keyword("BLOB");
18830                    }
18831                    Some(DialectType::Exasol) => {
18832                        // Exasol maps VARBINARY to VARCHAR
18833                        self.write_keyword("VARCHAR");
18834                    }
18835                    Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks) => {
18836                        // Spark/Hive use BINARY instead of VARBINARY
18837                        self.write_keyword("BINARY");
18838                    }
18839                    _ => {
18840                        self.write_keyword("VARBINARY");
18841                        if let Some(n) = length {
18842                            self.write(&format!("({})", n));
18843                        }
18844                    }
18845                }
18846            }
18847            DataType::Blob => {
18848                // Dialect-specific blob type mappings
18849                match self.config.dialect {
18850                    Some(DialectType::PostgreSQL) => self.write_keyword("BYTEA"),
18851                    Some(DialectType::Redshift) => self.write_keyword("VARBYTE"),
18852                    Some(DialectType::TSQL) | Some(DialectType::Fabric) => self.write_keyword("VARBINARY"),
18853                    Some(DialectType::BigQuery) => self.write_keyword("BYTES"),
18854                    Some(DialectType::Exasol) => self.write_keyword("VARCHAR"),
18855                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARBINARY"),
18856                    Some(DialectType::DuckDB) => {
18857                        // Python sqlglot: BLOB -> VARBINARY for DuckDB (base TYPE_MAPPING)
18858                        // DuckDB identity works via: BLOB -> transform VarBinary -> generator BLOB
18859                        self.write_keyword("VARBINARY");
18860                    }
18861                    Some(DialectType::Spark) | Some(DialectType::Databricks)
18862                    | Some(DialectType::Hive) => self.write_keyword("BINARY"),
18863                    Some(DialectType::ClickHouse) => self.write("Nullable(String)"),
18864                    _ => self.write_keyword("BLOB"),
18865                }
18866            }
18867            DataType::Bit { length } => {
18868                // Dialect-specific bit type mappings
18869                match self.config.dialect {
18870                    Some(DialectType::Dremio) | Some(DialectType::Spark)
18871                    | Some(DialectType::Databricks) | Some(DialectType::Hive)
18872                    | Some(DialectType::Snowflake) | Some(DialectType::BigQuery)
18873                    | Some(DialectType::Presto) | Some(DialectType::Trino)
18874                    | Some(DialectType::ClickHouse) | Some(DialectType::Redshift) => {
18875                        // These dialects don't support BIT type, use BOOLEAN
18876                        self.write_keyword("BOOLEAN");
18877                    }
18878                    _ => {
18879                        self.write_keyword("BIT");
18880                        if let Some(n) = length {
18881                            self.write(&format!("({})", n));
18882                        }
18883                    }
18884                }
18885            }
18886            DataType::VarBit { length } => {
18887                self.write_keyword("VARBIT");
18888                if let Some(n) = length {
18889                    self.write(&format!("({})", n));
18890                }
18891            }
18892            DataType::Date => self.write_keyword("DATE"),
18893            DataType::Time { precision, timezone } => {
18894                if *timezone {
18895                    // Dialect-specific TIME WITH TIME ZONE output
18896                    match self.config.dialect {
18897                        Some(DialectType::DuckDB) => {
18898                            // DuckDB: TIMETZ (drops precision)
18899                            self.write_keyword("TIMETZ");
18900                        }
18901                        Some(DialectType::PostgreSQL) => {
18902                            // PostgreSQL: TIMETZ or TIMETZ(p)
18903                            self.write_keyword("TIMETZ");
18904                            if let Some(p) = precision {
18905                                self.write(&format!("({})", p));
18906                            }
18907                        }
18908                        _ => {
18909                            // Presto/Trino/Redshift/others: TIME(p) WITH TIME ZONE
18910                            self.write_keyword("TIME");
18911                            if let Some(p) = precision {
18912                                self.write(&format!("({})", p));
18913                            }
18914                            self.write_keyword(" WITH TIME ZONE");
18915                        }
18916                    }
18917                } else {
18918                    // Spark/Hive/Databricks: TIME -> TIMESTAMP (TIME not supported)
18919                    if matches!(self.config.dialect, Some(DialectType::Spark)
18920                        | Some(DialectType::Databricks) | Some(DialectType::Hive)) {
18921                        self.write_keyword("TIMESTAMP");
18922                    } else {
18923                        self.write_keyword("TIME");
18924                        if let Some(p) = precision {
18925                            self.write(&format!("({})", p));
18926                        }
18927                    }
18928                }
18929            }
18930            DataType::Timestamp { precision, timezone } => {
18931                // Dialect-specific timestamp type mappings
18932                match self.config.dialect {
18933                    Some(DialectType::ClickHouse) => {
18934                        self.write("DateTime");
18935                        if let Some(p) = precision {
18936                            self.write(&format!("({})", p));
18937                        }
18938                    }
18939                    Some(DialectType::TSQL) => {
18940                        if *timezone {
18941                            self.write_keyword("DATETIMEOFFSET");
18942                        } else {
18943                            self.write_keyword("DATETIME2");
18944                        }
18945                        if let Some(p) = precision {
18946                            self.write(&format!("({})", p));
18947                        }
18948                    }
18949                    Some(DialectType::MySQL) => {
18950                        // MySQL uses TIMESTAMP for both cases
18951                        // MySQL's TIMESTAMP has implicit timezone behavior
18952                        self.write_keyword("TIMESTAMP");
18953                        if let Some(p) = precision {
18954                            self.write(&format!("({})", p));
18955                        }
18956                    }
18957                    Some(DialectType::BigQuery) => {
18958                        // BigQuery: TIMESTAMP is always UTC, DATETIME is timezone-naive
18959                        if *timezone {
18960                            self.write_keyword("TIMESTAMP");
18961                        } else {
18962                            self.write_keyword("DATETIME");
18963                        }
18964                    }
18965                    Some(DialectType::DuckDB) => {
18966                        // DuckDB: TIMESTAMPTZ shorthand
18967                        if *timezone {
18968                            self.write_keyword("TIMESTAMPTZ");
18969                        } else {
18970                            self.write_keyword("TIMESTAMP");
18971                            if let Some(p) = precision {
18972                                self.write(&format!("({})", p));
18973                            }
18974                        }
18975                    }
18976                    _ => {
18977                        if *timezone && !self.config.tz_to_with_time_zone {
18978                            // Use TIMESTAMPTZ shorthand when dialect doesn't prefer WITH TIME ZONE
18979                            self.write_keyword("TIMESTAMPTZ");
18980                            if let Some(p) = precision {
18981                                self.write(&format!("({})", p));
18982                            }
18983                        } else {
18984                            self.write_keyword("TIMESTAMP");
18985                            if let Some(p) = precision {
18986                                self.write(&format!("({})", p));
18987                            }
18988                            if *timezone {
18989                                self.write_space();
18990                                self.write_keyword("WITH TIME ZONE");
18991                            }
18992                        }
18993                    }
18994                }
18995            }
18996            DataType::Interval { unit, to } => {
18997                self.write_keyword("INTERVAL");
18998                if let Some(u) = unit {
18999                    self.write_space();
19000                    self.write_keyword(u);
19001                }
19002                // Handle range intervals like DAY TO HOUR
19003                if let Some(t) = to {
19004                    self.write_space();
19005                    self.write_keyword("TO");
19006                    self.write_space();
19007                    self.write_keyword(t);
19008                }
19009            }
19010            DataType::Json => {
19011                // Dialect-specific JSON type mappings
19012                match self.config.dialect {
19013                    Some(DialectType::Oracle) => self.write_keyword("JSON"), // Oracle 21c+
19014                    Some(DialectType::TSQL) => self.write_keyword("NVARCHAR(MAX)"), // No native JSON type
19015                    Some(DialectType::MySQL) => self.write_keyword("JSON"),
19016                    Some(DialectType::Snowflake) => self.write_keyword("VARIANT"),
19017                    _ => self.write_keyword("JSON"),
19018                }
19019            }
19020            DataType::JsonB => {
19021                // JSONB is PostgreSQL specific, but Doris also supports it
19022                match self.config.dialect {
19023                    Some(DialectType::PostgreSQL) => self.write_keyword("JSONB"),
19024                    Some(DialectType::Doris) => self.write_keyword("JSONB"),
19025                    Some(DialectType::Snowflake) => self.write_keyword("VARIANT"),
19026                    Some(DialectType::TSQL) => self.write_keyword("NVARCHAR(MAX)"),
19027                    Some(DialectType::DuckDB) => self.write_keyword("JSON"), // DuckDB maps JSONB to JSON
19028                    _ => self.write_keyword("JSON"), // Fall back to JSON for other dialects
19029                }
19030            }
19031            DataType::Uuid => {
19032                // Dialect-specific UUID type mappings
19033                match self.config.dialect {
19034                    Some(DialectType::TSQL) => self.write_keyword("UNIQUEIDENTIFIER"),
19035                    Some(DialectType::MySQL) => self.write_keyword("CHAR(36)"),
19036                    Some(DialectType::Oracle) => self.write_keyword("RAW(16)"),
19037                    Some(DialectType::BigQuery) | Some(DialectType::Spark) | Some(DialectType::Databricks) => self.write_keyword("STRING"),
19038                    _ => self.write_keyword("UUID"),
19039                }
19040            }
19041            DataType::Array { element_type, dimension } => {
19042                // Dialect-specific array syntax
19043                match self.config.dialect {
19044                    Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) | Some(DialectType::DuckDB) => {
19045                        // PostgreSQL uses TYPE[] or TYPE[N] syntax
19046                        self.generate_data_type(element_type)?;
19047                        if let Some(dim) = dimension {
19048                            self.write(&format!("[{}]", dim));
19049                        } else {
19050                            self.write("[]");
19051                        }
19052                    }
19053                    Some(DialectType::BigQuery) => {
19054                        self.write_keyword("ARRAY<");
19055                        self.generate_data_type(element_type)?;
19056                        self.write(">");
19057                    }
19058                    Some(DialectType::Snowflake) | Some(DialectType::Presto) | Some(DialectType::Trino)
19059                    | Some(DialectType::ClickHouse) => {
19060                        // These dialects use Array(TYPE) parentheses syntax
19061                        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
19062                            self.write("Array(");
19063                        } else {
19064                            self.write_keyword("ARRAY(");
19065                        }
19066                        self.generate_data_type(element_type)?;
19067                        self.write(")");
19068                    }
19069                    Some(DialectType::TSQL) | Some(DialectType::MySQL) | Some(DialectType::Oracle) => {
19070                        // These dialects don't have native array types
19071                        // Fall back to JSON or use native workarounds
19072                        match self.config.dialect {
19073                            Some(DialectType::MySQL) => self.write_keyword("JSON"),
19074                            Some(DialectType::TSQL) => self.write_keyword("NVARCHAR(MAX)"),
19075                            _ => self.write_keyword("JSON"),
19076                        }
19077                    }
19078                    _ => {
19079                        // Default: use angle bracket syntax (ARRAY<T>)
19080                        self.write_keyword("ARRAY<");
19081                        self.generate_data_type(element_type)?;
19082                        self.write(">");
19083                    }
19084                }
19085            }
19086            DataType::List { element_type } => {
19087                // Materialize: element_type LIST (postfix syntax)
19088                self.generate_data_type(element_type)?;
19089                self.write_keyword(" LIST");
19090            }
19091            DataType::Map { key_type, value_type } => {
19092                // Use parentheses for Snowflake and RisingWave, bracket syntax for Materialize, angle brackets for others
19093                match self.config.dialect {
19094                    Some(DialectType::Materialize) => {
19095                        // Materialize: MAP[key_type => value_type]
19096                        self.write_keyword("MAP[");
19097                        self.generate_data_type(key_type)?;
19098                        self.write(" => ");
19099                        self.generate_data_type(value_type)?;
19100                        self.write("]");
19101                    }
19102                    Some(DialectType::Snowflake) | Some(DialectType::RisingWave) | Some(DialectType::DuckDB)
19103                    | Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => {
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                    _ => {
19111                        self.write_keyword("MAP<");
19112                        self.generate_data_type(key_type)?;
19113                        self.write(", ");
19114                        self.generate_data_type(value_type)?;
19115                        self.write(">");
19116                    }
19117                }
19118            }
19119            DataType::Vector { element_type, dimension } => {
19120                if matches!(self.config.dialect, Some(DialectType::SingleStore)) {
19121                    // SingleStore format: VECTOR(dimension, type_alias)
19122                    self.write_keyword("VECTOR(");
19123                    if let Some(dim) = dimension {
19124                        self.write(&dim.to_string());
19125                    }
19126                    // Map type back to SingleStore alias
19127                    let type_alias = element_type.as_ref().and_then(|et| match et.as_ref() {
19128                        DataType::TinyInt { .. } => Some("I8"),
19129                        DataType::SmallInt { .. } => Some("I16"),
19130                        DataType::Int { .. } => Some("I32"),
19131                        DataType::BigInt { .. } => Some("I64"),
19132                        DataType::Float { .. } => Some("F32"),
19133                        DataType::Double { .. } => Some("F64"),
19134                        _ => None,
19135                    });
19136                    if let Some(alias) = type_alias {
19137                        if dimension.is_some() {
19138                            self.write(", ");
19139                        }
19140                        self.write(alias);
19141                    }
19142                    self.write(")");
19143                } else {
19144                    // Snowflake format: VECTOR(type, dimension)
19145                    self.write_keyword("VECTOR(");
19146                    if let Some(ref et) = element_type {
19147                        self.generate_data_type(et)?;
19148                        if dimension.is_some() {
19149                            self.write(", ");
19150                        }
19151                    }
19152                    if let Some(dim) = dimension {
19153                        self.write(&dim.to_string());
19154                    }
19155                    self.write(")");
19156                }
19157            }
19158            DataType::Object { fields, modifier } => {
19159                self.write_keyword("OBJECT(");
19160                for (i, (name, dt, not_null)) in fields.iter().enumerate() {
19161                    if i > 0 {
19162                        self.write(", ");
19163                    }
19164                    self.write(name);
19165                    self.write(" ");
19166                    self.generate_data_type(dt)?;
19167                    if *not_null {
19168                        self.write_keyword(" NOT NULL");
19169                    }
19170                }
19171                self.write(")");
19172                if let Some(mod_str) = modifier {
19173                    self.write(" ");
19174                    self.write_keyword(mod_str);
19175                }
19176            }
19177            DataType::Struct { fields, nested } => {
19178                // Dialect-specific struct type mappings
19179                match self.config.dialect {
19180                    Some(DialectType::Snowflake) => {
19181                        // Snowflake maps STRUCT to OBJECT
19182                        self.write_keyword("OBJECT(");
19183                        for (i, field) in fields.iter().enumerate() {
19184                            if i > 0 {
19185                                self.write(", ");
19186                            }
19187                            if !field.name.is_empty() {
19188                                self.write(&field.name);
19189                                self.write(" ");
19190                            }
19191                            self.generate_data_type(&field.data_type)?;
19192                        }
19193                        self.write(")");
19194                    }
19195                    Some(DialectType::Presto) | Some(DialectType::Trino) => {
19196                        // Presto/Trino use ROW(name TYPE, ...) syntax
19197                        self.write_keyword("ROW(");
19198                        for (i, field) in fields.iter().enumerate() {
19199                            if i > 0 {
19200                                self.write(", ");
19201                            }
19202                            if !field.name.is_empty() {
19203                                self.write(&field.name);
19204                                self.write(" ");
19205                            }
19206                            self.generate_data_type(&field.data_type)?;
19207                        }
19208                        self.write(")");
19209                    }
19210                    Some(DialectType::DuckDB) => {
19211                        // DuckDB uses parenthesized syntax: STRUCT(name TYPE, ...)
19212                        self.write_keyword("STRUCT(");
19213                        for (i, field) in fields.iter().enumerate() {
19214                            if i > 0 {
19215                                self.write(", ");
19216                            }
19217                            if !field.name.is_empty() {
19218                                self.write(&field.name);
19219                                self.write(" ");
19220                            }
19221                            self.generate_data_type(&field.data_type)?;
19222                        }
19223                        self.write(")");
19224                    }
19225                    Some(DialectType::SingleStore) => {
19226                        // SingleStore uses RECORD(name TYPE, ...) for struct types
19227                        self.write_keyword("RECORD(");
19228                        for (i, field) in fields.iter().enumerate() {
19229                            if i > 0 {
19230                                self.write(", ");
19231                            }
19232                            if !field.name.is_empty() {
19233                                self.write(&field.name);
19234                                self.write(" ");
19235                            }
19236                            self.generate_data_type(&field.data_type)?;
19237                        }
19238                        self.write(")");
19239                    }
19240                    _ => {
19241                        // Hive/Spark always use angle bracket syntax: STRUCT<name: TYPE>
19242                        let force_angle_brackets = matches!(
19243                            self.config.dialect,
19244                            Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)
19245                        );
19246                        if *nested && !force_angle_brackets {
19247                            self.write_keyword("STRUCT(");
19248                            for (i, field) in fields.iter().enumerate() {
19249                                if i > 0 {
19250                                    self.write(", ");
19251                                }
19252                                if !field.name.is_empty() {
19253                                    self.write(&field.name);
19254                                    self.write(" ");
19255                                }
19256                                self.generate_data_type(&field.data_type)?;
19257                            }
19258                            self.write(")");
19259                        } else {
19260                            self.write_keyword("STRUCT<");
19261                            for (i, field) in fields.iter().enumerate() {
19262                                if i > 0 {
19263                                    self.write(", ");
19264                                }
19265                                if !field.name.is_empty() {
19266                                    // Named field: name TYPE (with configurable separator for Hive)
19267                                    self.write(&field.name);
19268                                    self.write(self.config.struct_field_sep);
19269                                }
19270                                // For anonymous fields, just output the type
19271                                self.generate_data_type(&field.data_type)?;
19272                                // Spark/Databricks: Output COMMENT clause if present
19273                                if let Some(comment) = &field.comment {
19274                                    self.write(" COMMENT '");
19275                                    self.write(comment);
19276                                    self.write("'");
19277                                }
19278                                // BigQuery: Output OPTIONS clause if present
19279                                if !field.options.is_empty() {
19280                                    self.write(" ");
19281                                    self.generate_options_clause(&field.options)?;
19282                                }
19283                            }
19284                            self.write(">");
19285                        }
19286                    }
19287                }
19288            }
19289            DataType::Enum { values, assignments } => {
19290                // DuckDB ENUM type: ENUM('RED', 'GREEN', 'BLUE')
19291                // ClickHouse: Enum('hello' = 1, 'world' = 2)
19292                if self.config.dialect == Some(DialectType::ClickHouse) {
19293                    self.write("Enum(");
19294                } else {
19295                    self.write_keyword("ENUM(");
19296                }
19297                for (i, val) in values.iter().enumerate() {
19298                    if i > 0 {
19299                        self.write(", ");
19300                    }
19301                    self.write("'");
19302                    self.write(val);
19303                    self.write("'");
19304                    if let Some(Some(assignment)) = assignments.get(i) {
19305                        self.write(" = ");
19306                        self.write(assignment);
19307                    }
19308                }
19309                self.write(")");
19310            }
19311            DataType::Set { values } => {
19312                // MySQL SET type: SET('a', 'b', 'c')
19313                self.write_keyword("SET(");
19314                for (i, val) in values.iter().enumerate() {
19315                    if i > 0 {
19316                        self.write(", ");
19317                    }
19318                    self.write("'");
19319                    self.write(val);
19320                    self.write("'");
19321                }
19322                self.write(")");
19323            }
19324            DataType::Union { fields } => {
19325                // DuckDB UNION type: UNION(num INT, str TEXT)
19326                self.write_keyword("UNION(");
19327                for (i, (name, dt)) in fields.iter().enumerate() {
19328                    if i > 0 {
19329                        self.write(", ");
19330                    }
19331                    if !name.is_empty() {
19332                        self.write(name);
19333                        self.write(" ");
19334                    }
19335                    self.generate_data_type(dt)?;
19336                }
19337                self.write(")");
19338            }
19339            DataType::Custom { name } => {
19340                // Handle dialect-specific type transformations
19341                let name_upper = name.to_uppercase();
19342                match self.config.dialect {
19343                    Some(DialectType::ClickHouse) => {
19344                        let (base_upper, suffix) = if let Some(idx) = name.find('(') {
19345                            (name_upper[..idx].to_string(), &name[idx..])
19346                        } else {
19347                            (name_upper.clone(), "")
19348                        };
19349                        let mapped = match base_upper.as_str() {
19350                            "DATETIME" | "TIMESTAMPTZ" | "TIMESTAMP" | "TIMESTAMPNTZ" | "SMALLDATETIME" | "DATETIME2" => "DateTime",
19351                            "DATETIME64" => "DateTime64",
19352                            "DATE32" => "Date32",
19353                            "INT" => "Int32",
19354                            "MEDIUMINT" => "Int32",
19355                            "INT8" => "Int8",
19356                            "INT16" => "Int16",
19357                            "INT32" => "Int32",
19358                            "INT64" => "Int64",
19359                            "INT128" => "Int128",
19360                            "INT256" => "Int256",
19361                            "UINT8" => "UInt8",
19362                            "UINT16" => "UInt16",
19363                            "UINT32" => "UInt32",
19364                            "UINT64" => "UInt64",
19365                            "UINT128" => "UInt128",
19366                            "UINT256" => "UInt256",
19367                            "FLOAT32" => "Float32",
19368                            "FLOAT64" => "Float64",
19369                            "DECIMAL32" => "Decimal32",
19370                            "DECIMAL64" => "Decimal64",
19371                            "DECIMAL128" => "Decimal128",
19372                            "DECIMAL256" => "Decimal256",
19373                            "ENUM" => "Enum",
19374                            "ENUM8" => "Enum8",
19375                            "ENUM16" => "Enum16",
19376                            "FIXEDSTRING" => "FixedString",
19377                            "NESTED" => "Nested",
19378                            "LOWCARDINALITY" => "LowCardinality",
19379                            "NULLABLE" => "Nullable",
19380                            "IPV4" => "IPv4",
19381                            "IPV6" => "IPv6",
19382                            "POINT" => "Point",
19383                            "RING" => "Ring",
19384                            "LINESTRING" => "LineString",
19385                            "MULTILINESTRING" => "MultiLineString",
19386                            "POLYGON" => "Polygon",
19387                            "MULTIPOLYGON" => "MultiPolygon",
19388                            "AGGREGATEFUNCTION" => "AggregateFunction",
19389                            "SIMPLEAGGREGATEFUNCTION" => "SimpleAggregateFunction",
19390                            "DYNAMIC" => "Dynamic",
19391                            _ => "",
19392                        };
19393                        if mapped.is_empty() {
19394                            self.write(name);
19395                        } else {
19396                            self.write(mapped);
19397                            self.write(suffix);
19398                        }
19399                    }
19400                    Some(DialectType::MySQL) if name_upper == "TIMESTAMPTZ" || name_upper == "TIMESTAMPLTZ" => {
19401                        // MySQL doesn't support TIMESTAMPTZ/TIMESTAMPLTZ, use TIMESTAMP
19402                        self.write_keyword("TIMESTAMP");
19403                    }
19404                    Some(DialectType::TSQL) if name_upper == "VARIANT" => {
19405                        self.write_keyword("SQL_VARIANT");
19406                    }
19407                    Some(DialectType::DuckDB) if name_upper == "DECFLOAT" => {
19408                        self.write_keyword("DECIMAL(38, 5)");
19409                    }
19410                    Some(DialectType::Exasol) => {
19411                        // Exasol type mappings for custom types
19412                        match name_upper.as_str() {
19413                            // Binary types → VARCHAR
19414                            "LONGBLOB" | "MEDIUMBLOB" | "TINYBLOB" => self.write_keyword("VARCHAR"),
19415                            // Text types → VARCHAR (TEXT → LONG VARCHAR is handled by DataType::Text)
19416                            "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" => self.write_keyword("VARCHAR"),
19417                            // Integer types
19418                            "MEDIUMINT" => self.write_keyword("INT"),
19419                            // Decimal types → DECIMAL
19420                            "DECIMAL32" | "DECIMAL64" | "DECIMAL128" | "DECIMAL256" => self.write_keyword("DECIMAL"),
19421                            // Timestamp types
19422                            "DATETIME" => self.write_keyword("TIMESTAMP"),
19423                            "TIMESTAMPLTZ" => self.write_keyword("TIMESTAMP WITH LOCAL TIME ZONE"),
19424                            _ => self.write(name),
19425                        }
19426                    }
19427                    Some(DialectType::Dremio) => {
19428                        // Dremio type mappings for custom types
19429                        match name_upper.as_str() {
19430                            "TIMESTAMPNTZ" | "DATETIME" => self.write_keyword("TIMESTAMP"),
19431                            "ARRAY" => self.write_keyword("LIST"),
19432                            "NCHAR" => self.write_keyword("VARCHAR"),
19433                            _ => self.write(name),
19434                        }
19435                    }
19436                    // Map dialect-specific custom types to standard SQL types for other dialects
19437                    _ => {
19438                        // Extract base name and args for types with parenthesized args (e.g., DATETIME2(3))
19439                        let (base_upper, _args_str) = if let Some(idx) = name_upper.find('(') {
19440                            (name_upper[..idx].to_string(), Some(&name[idx..]))
19441                        } else {
19442                            (name_upper.clone(), None)
19443                        };
19444
19445                        match base_upper.as_str() {
19446                            "INT64" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19447                                self.write_keyword("BIGINT");
19448                            }
19449                            "FLOAT64" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19450                                self.write_keyword("DOUBLE");
19451                            }
19452                            "BOOL" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19453                                self.write_keyword("BOOLEAN");
19454                            }
19455                            "BYTES" if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)) => {
19456                                self.write_keyword("BINARY");
19457                            }
19458                            "BYTES" if !matches!(self.config.dialect, Some(DialectType::BigQuery)) => {
19459                                self.write_keyword("VARBINARY");
19460                            }
19461                            // TSQL DATETIME2/SMALLDATETIME -> TIMESTAMP
19462                            "DATETIME2" | "SMALLDATETIME" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19463                                // PostgreSQL preserves precision, others don't
19464                                if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
19465                                    self.write_keyword("TIMESTAMP");
19466                                    if let Some(args) = _args_str {
19467                                        self.write(args);
19468                                    }
19469                                } else {
19470                                    self.write_keyword("TIMESTAMP");
19471                                }
19472                            }
19473                            // TSQL DATETIMEOFFSET -> TIMESTAMPTZ
19474                            "DATETIMEOFFSET" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19475                                if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
19476                                    self.write_keyword("TIMESTAMPTZ");
19477                                    if let Some(args) = _args_str {
19478                                        self.write(args);
19479                                    }
19480                                } else {
19481                                    self.write_keyword("TIMESTAMPTZ");
19482                                }
19483                            }
19484                            // TSQL UNIQUEIDENTIFIER -> UUID or STRING
19485                            "UNIQUEIDENTIFIER" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19486                                match self.config.dialect {
19487                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19488                                    | Some(DialectType::Hive) => self.write_keyword("STRING"),
19489                                    _ => self.write_keyword("UUID"),
19490                                }
19491                            }
19492                            // TSQL BIT -> BOOLEAN for most dialects
19493                            "BIT" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)
19494                                | Some(DialectType::PostgreSQL) | Some(DialectType::MySQL) | Some(DialectType::DuckDB)) => {
19495                                self.write_keyword("BOOLEAN");
19496                            }
19497                            // TSQL NVARCHAR -> VARCHAR (with default size 30 for some dialects)
19498                            "NVARCHAR" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19499                                match self.config.dialect {
19500                                    Some(DialectType::SQLite) => {
19501                                        self.write_keyword("TEXT");
19502                                        if let Some(args) = _args_str {
19503                                            self.write(args);
19504                                        }
19505                                    }
19506                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19507                                    | Some(DialectType::Hive) => {
19508                                        if _args_str.is_some() {
19509                                            self.write_keyword("VARCHAR");
19510                                            self.write(_args_str.unwrap());
19511                                        } else {
19512                                            self.write_keyword("VARCHAR(30)");
19513                                        }
19514                                    }
19515                                    _ => {
19516                                        self.write_keyword("VARCHAR");
19517                                        if let Some(args) = _args_str {
19518                                            self.write(args);
19519                                        }
19520                                    }
19521                                }
19522                            }
19523                            // TSQL NCHAR -> CHAR
19524                            "NCHAR" if !matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) => {
19525                                match self.config.dialect {
19526                                    Some(DialectType::Spark) | Some(DialectType::Databricks)
19527                                    | Some(DialectType::Hive) => {
19528                                        if _args_str.is_some() {
19529                                            self.write_keyword("CHAR");
19530                                            self.write(_args_str.unwrap());
19531                                        } else {
19532                                            self.write_keyword("CHAR(30)");
19533                                        }
19534                                    }
19535                                    _ => {
19536                                        self.write_keyword("CHAR");
19537                                        if let Some(args) = _args_str {
19538                                            self.write(args);
19539                                        }
19540                                    }
19541                                }
19542                            }
19543                            // MySQL text variant types -> map to appropriate target type
19544                            // For MySQL/SingleStore: keep original name (column definitions), CAST handling is in generate_cast
19545                            "LONGTEXT" | "MEDIUMTEXT" | "TINYTEXT" => {
19546                                match self.config.dialect {
19547                                    Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB) => self.write_keyword(&base_upper),
19548                                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => self.write_keyword("TEXT"),
19549                                    Some(DialectType::BigQuery) => self.write_keyword("STRING"),
19550                                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARCHAR"),
19551                                    Some(DialectType::Snowflake) | Some(DialectType::Redshift) | Some(DialectType::Dremio) => self.write_keyword("VARCHAR"),
19552                                    _ => self.write_keyword("TEXT"),
19553                                }
19554                            }
19555                            // MySQL blob variant types -> map to appropriate target type
19556                            // For MySQL/SingleStore: keep original name (column definitions), CAST handling is in generate_cast
19557                            "LONGBLOB" | "MEDIUMBLOB" | "TINYBLOB" => {
19558                                match self.config.dialect {
19559                                    Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::TiDB) => self.write_keyword(&base_upper),
19560                                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => self.write_keyword("BLOB"),
19561                                    Some(DialectType::DuckDB) => self.write_keyword("VARBINARY"),
19562                                    Some(DialectType::BigQuery) => self.write_keyword("BYTES"),
19563                                    Some(DialectType::Presto) | Some(DialectType::Trino) | Some(DialectType::Athena) => self.write_keyword("VARBINARY"),
19564                                    Some(DialectType::Snowflake) | Some(DialectType::Redshift) | Some(DialectType::Dremio) => self.write_keyword("VARBINARY"),
19565                                    _ => self.write_keyword("BLOB"),
19566                                }
19567                            }
19568                            // LONGVARCHAR -> TEXT for SQLite, VARCHAR for others
19569                            "LONGVARCHAR" => {
19570                                match self.config.dialect {
19571                                    Some(DialectType::SQLite) => self.write_keyword("TEXT"),
19572                                    _ => self.write_keyword("VARCHAR"),
19573                                }
19574                            }
19575                            _ => self.write(name),
19576                        }
19577                    }
19578                }
19579            }
19580            DataType::Geometry { subtype, srid } => {
19581                // Dialect-specific geometry type mappings
19582                match self.config.dialect {
19583                    Some(DialectType::MySQL) => {
19584                        // MySQL uses POINT SRID 4326 syntax for specific types
19585                        if let Some(sub) = subtype {
19586                            self.write_keyword(sub);
19587                            if let Some(s) = srid {
19588                                self.write(" SRID ");
19589                                self.write(&s.to_string());
19590                            }
19591                        } else {
19592                            self.write_keyword("GEOMETRY");
19593                        }
19594                    }
19595                    Some(DialectType::BigQuery) => {
19596                        // BigQuery only supports GEOGRAPHY, not GEOMETRY
19597                        self.write_keyword("GEOGRAPHY");
19598                    }
19599                    Some(DialectType::Teradata) => {
19600                        // Teradata uses ST_GEOMETRY
19601                        self.write_keyword("ST_GEOMETRY");
19602                        if subtype.is_some() || srid.is_some() {
19603                            self.write("(");
19604                            if let Some(sub) = subtype {
19605                                self.write_keyword(sub);
19606                            }
19607                            if let Some(s) = srid {
19608                                if subtype.is_some() {
19609                                    self.write(", ");
19610                                }
19611                                self.write(&s.to_string());
19612                            }
19613                            self.write(")");
19614                        }
19615                    }
19616                    _ => {
19617                        // PostgreSQL, Snowflake, DuckDB use GEOMETRY(subtype, srid) syntax
19618                        self.write_keyword("GEOMETRY");
19619                        if subtype.is_some() || srid.is_some() {
19620                            self.write("(");
19621                            if let Some(sub) = subtype {
19622                                self.write_keyword(sub);
19623                            }
19624                            if let Some(s) = srid {
19625                                if subtype.is_some() {
19626                                    self.write(", ");
19627                                }
19628                                self.write(&s.to_string());
19629                            }
19630                            self.write(")");
19631                        }
19632                    }
19633                }
19634            }
19635            DataType::Geography { subtype, srid } => {
19636                // Dialect-specific geography type mappings
19637                match self.config.dialect {
19638                    Some(DialectType::MySQL) => {
19639                        // MySQL doesn't have native GEOGRAPHY, use GEOMETRY with SRID 4326
19640                        if let Some(sub) = subtype {
19641                            self.write_keyword(sub);
19642                        } else {
19643                            self.write_keyword("GEOMETRY");
19644                        }
19645                        // Geography implies SRID 4326 (WGS84)
19646                        let effective_srid = srid.unwrap_or(4326);
19647                        self.write(" SRID ");
19648                        self.write(&effective_srid.to_string());
19649                    }
19650                    Some(DialectType::BigQuery) => {
19651                        // BigQuery uses simple GEOGRAPHY without parameters
19652                        self.write_keyword("GEOGRAPHY");
19653                    }
19654                    Some(DialectType::Snowflake) => {
19655                        // Snowflake uses GEOGRAPHY without parameters
19656                        self.write_keyword("GEOGRAPHY");
19657                    }
19658                    _ => {
19659                        // PostgreSQL uses GEOGRAPHY(subtype, srid) syntax
19660                        self.write_keyword("GEOGRAPHY");
19661                        if subtype.is_some() || srid.is_some() {
19662                            self.write("(");
19663                            if let Some(sub) = subtype {
19664                                self.write_keyword(sub);
19665                            }
19666                            if let Some(s) = srid {
19667                                if subtype.is_some() {
19668                                    self.write(", ");
19669                                }
19670                                self.write(&s.to_string());
19671                            }
19672                            self.write(")");
19673                        }
19674                    }
19675                }
19676            }
19677            DataType::CharacterSet { name } => {
19678                // For MySQL CONVERT USING - output as CHAR CHARACTER SET name
19679                self.write_keyword("CHAR CHARACTER SET ");
19680                self.write(name);
19681            }
19682            _ => self.write("UNKNOWN"),
19683        }
19684        Ok(())
19685    }
19686
19687    // === Helper methods ===
19688
19689    fn write(&mut self, s: &str) {
19690        self.output.push_str(s);
19691    }
19692
19693    fn write_space(&mut self) {
19694        self.output.push(' ');
19695    }
19696
19697    fn write_keyword(&mut self, keyword: &str) {
19698        if self.config.uppercase_keywords {
19699            self.output.push_str(keyword);
19700        } else {
19701            self.output.push_str(&keyword.to_lowercase());
19702        }
19703    }
19704
19705    /// Convert strptime format string to Exasol format string
19706    /// Exasol TIME_MAPPING (reverse of Python sqlglot):
19707    /// %Y -> YYYY, %y -> YY, %m -> MM, %d -> DD, %H -> HH, %M -> MI, %S -> SS, %a -> DY
19708    fn convert_strptime_to_exasol_format(format: &str) -> String {
19709        let mut result = String::new();
19710        let chars: Vec<char> = format.chars().collect();
19711        let mut i = 0;
19712        while i < chars.len() {
19713            if chars[i] == '%' && i + 1 < chars.len() {
19714                let spec = chars[i + 1];
19715                let exasol_spec = match spec {
19716                    'Y' => "YYYY",
19717                    'y' => "YY",
19718                    'm' => "MM",
19719                    'd' => "DD",
19720                    'H' => "HH",
19721                    'M' => "MI",
19722                    'S' => "SS",
19723                    'a' => "DY",   // abbreviated weekday name
19724                    'A' => "DAY",  // full weekday name
19725                    'b' => "MON",  // abbreviated month name
19726                    'B' => "MONTH", // full month name
19727                    'I' => "H12",  // 12-hour format
19728                    'u' => "ID",   // ISO weekday (1-7)
19729                    'V' => "IW",   // ISO week number
19730                    'G' => "IYYY", // ISO year
19731                    'W' => "UW",   // Week number (Monday as first day)
19732                    'U' => "UW",   // Week number (Sunday as first day)
19733                    'z' => "Z",    // timezone offset
19734                    _ => {
19735                        // Unknown specifier, keep as-is
19736                        result.push('%');
19737                        result.push(spec);
19738                        i += 2;
19739                        continue;
19740                    }
19741                };
19742                result.push_str(exasol_spec);
19743                i += 2;
19744            } else {
19745                result.push(chars[i]);
19746                i += 1;
19747            }
19748        }
19749        result
19750    }
19751
19752    /// Convert strptime format string to PostgreSQL/Redshift format string
19753    /// PostgreSQL INVERSE_TIME_MAPPING from Python sqlglot:
19754    /// %Y -> YYYY, %y -> YY, %m -> MM, %d -> DD, %H -> HH24, %M -> MI, %S -> SS, %f -> US, etc.
19755    fn convert_strptime_to_postgres_format(format: &str) -> String {
19756        let mut result = String::new();
19757        let chars: Vec<char> = format.chars().collect();
19758        let mut i = 0;
19759        while i < chars.len() {
19760            if chars[i] == '%' && i + 1 < chars.len() {
19761                let spec = chars[i + 1];
19762                let pg_spec = match spec {
19763                    'Y' => "YYYY",
19764                    'y' => "YY",
19765                    'm' => "MM",
19766                    'd' => "DD",
19767                    'H' => "HH24",
19768                    'I' => "HH12",
19769                    'M' => "MI",
19770                    'S' => "SS",
19771                    'f' => "US",     // microseconds
19772                    'u' => "D",      // day of week (1=Monday)
19773                    'j' => "DDD",    // day of year
19774                    'z' => "OF",     // UTC offset
19775                    'Z' => "TZ",     // timezone name
19776                    'A' => "TMDay",  // full weekday name
19777                    'a' => "TMDy",   // abbreviated weekday name
19778                    'b' => "TMMon",  // abbreviated month name
19779                    'B' => "TMMonth", // full month name
19780                    'U' => "WW",     // week number
19781                    _ => {
19782                        // Unknown specifier, keep as-is
19783                        result.push('%');
19784                        result.push(spec);
19785                        i += 2;
19786                        continue;
19787                    }
19788                };
19789                result.push_str(pg_spec);
19790                i += 2;
19791            } else {
19792                result.push(chars[i]);
19793                i += 1;
19794            }
19795        }
19796        result
19797    }
19798
19799    /// Write a LIMIT expression value, evaluating constant expressions if limit_only_literals is set
19800    fn write_limit_expr(&mut self, expr: &Expression) -> Result<()> {
19801        if self.config.limit_only_literals {
19802            if let Some(value) = Self::try_evaluate_constant(expr) {
19803                self.write(&value.to_string());
19804                return Ok(());
19805            }
19806        }
19807        self.generate_expression(expr)
19808    }
19809
19810    /// Format a comment with proper spacing.
19811    /// Converts `/*text*/` to `/* text */` (adding internal spaces if not present).
19812    /// Python SQLGlot normalizes comment format to have spaces inside block comments.
19813    fn write_formatted_comment(&mut self, comment: &str) {
19814        // Normalize all comments to block comment format /* ... */
19815        // This matches Python sqlglot behavior which always outputs block comments
19816        let content = if comment.starts_with("/*") && comment.ends_with("*/") {
19817            // Already block comment - extract inner content
19818            comment[2..comment.len() - 2].trim()
19819        } else if comment.starts_with("--") {
19820            // Line comment - extract content after --
19821            comment[2..].trim()
19822        } else {
19823            // Raw content (no delimiters)
19824            comment.trim()
19825        };
19826        self.output.push_str("/* ");
19827        self.output.push_str(content);
19828        self.output.push_str(" */");
19829    }
19830
19831    /// Escape a raw block content (from dollar-quoted string) for single-quoted output.
19832    /// Escapes single quotes with backslash, and for Snowflake also escapes backslashes.
19833    fn escape_block_for_single_quote(&self, block: &str) -> String {
19834        let escape_backslash = matches!(
19835            self.config.dialect,
19836            Some(crate::dialects::DialectType::Snowflake)
19837        );
19838        let mut escaped = String::with_capacity(block.len() + 4);
19839        for ch in block.chars() {
19840            if ch == '\'' {
19841                escaped.push('\\');
19842                escaped.push('\'');
19843            } else if escape_backslash && ch == '\\' {
19844                escaped.push('\\');
19845                escaped.push('\\');
19846            } else {
19847                escaped.push(ch);
19848            }
19849        }
19850        escaped
19851    }
19852
19853    fn write_newline(&mut self) {
19854        self.output.push('\n');
19855    }
19856
19857    fn write_indent(&mut self) {
19858        for _ in 0..self.indent_level {
19859            self.output.push_str(&self.config.indent);
19860        }
19861    }
19862
19863    // === SQLGlot-style pretty printing helpers ===
19864
19865    /// Returns the separator string for pretty printing.
19866    /// Check if the total length of arguments exceeds max_text_width.
19867    /// Used for dynamic line breaking in expressions() formatting.
19868    fn too_wide(&self, args: &[String]) -> bool {
19869        args.iter().map(|s| s.len()).sum::<usize>() > self.config.max_text_width
19870    }
19871
19872    /// Writes a clause with a single condition (WHERE, HAVING, QUALIFY).
19873    /// In pretty mode: newline + indented keyword + newline + indented condition
19874    fn write_clause_condition(&mut self, keyword: &str, condition: &Expression) -> Result<()> {
19875        if self.config.pretty {
19876            self.write_newline();
19877            self.write_indent();
19878            self.write_keyword(keyword);
19879            self.write_newline();
19880            self.indent_level += 1;
19881            self.write_indent();
19882            self.generate_expression(condition)?;
19883            self.indent_level -= 1;
19884        } else {
19885            self.write_space();
19886            self.write_keyword(keyword);
19887            self.write_space();
19888            self.generate_expression(condition)?;
19889        }
19890        Ok(())
19891    }
19892
19893    /// Writes a clause with a list of expressions (GROUP BY, DISTRIBUTE BY, CLUSTER BY).
19894    /// In pretty mode: each expression on new line with indentation
19895    fn write_clause_expressions(&mut self, keyword: &str, exprs: &[Expression]) -> Result<()> {
19896        if exprs.is_empty() {
19897            return Ok(());
19898        }
19899
19900        if self.config.pretty {
19901            self.write_newline();
19902            self.write_indent();
19903            self.write_keyword(keyword);
19904            self.write_newline();
19905            self.indent_level += 1;
19906            for (i, expr) in exprs.iter().enumerate() {
19907                if i > 0 {
19908                    self.write(",");
19909                    self.write_newline();
19910                }
19911                self.write_indent();
19912                self.generate_expression(expr)?;
19913            }
19914            self.indent_level -= 1;
19915        } else {
19916            self.write_space();
19917            self.write_keyword(keyword);
19918            self.write_space();
19919            for (i, expr) in exprs.iter().enumerate() {
19920                if i > 0 {
19921                    self.write(", ");
19922                }
19923                self.generate_expression(expr)?;
19924            }
19925        }
19926        Ok(())
19927    }
19928
19929    /// Writes ORDER BY / SORT BY clause with Ordered expressions
19930    fn write_order_clause(&mut self, keyword: &str, orderings: &[Ordered]) -> Result<()> {
19931        if orderings.is_empty() {
19932            return Ok(());
19933        }
19934
19935        if self.config.pretty {
19936            self.write_newline();
19937            self.write_indent();
19938            self.write_keyword(keyword);
19939            self.write_newline();
19940            self.indent_level += 1;
19941            for (i, ordered) in orderings.iter().enumerate() {
19942                if i > 0 {
19943                    self.write(",");
19944                    self.write_newline();
19945                }
19946                self.write_indent();
19947                self.generate_ordered(ordered)?;
19948            }
19949            self.indent_level -= 1;
19950        } else {
19951            self.write_space();
19952            self.write_keyword(keyword);
19953            self.write_space();
19954            for (i, ordered) in orderings.iter().enumerate() {
19955                if i > 0 {
19956                    self.write(", ");
19957                }
19958                self.generate_ordered(ordered)?;
19959            }
19960        }
19961        Ok(())
19962    }
19963
19964    /// Writes WINDOW clause with named window definitions
19965    fn write_window_clause(&mut self, windows: &[NamedWindow]) -> Result<()> {
19966        if windows.is_empty() {
19967            return Ok(());
19968        }
19969
19970        if self.config.pretty {
19971            self.write_newline();
19972            self.write_indent();
19973            self.write_keyword("WINDOW");
19974            self.write_newline();
19975            self.indent_level += 1;
19976            for (i, named_window) in windows.iter().enumerate() {
19977                if i > 0 {
19978                    self.write(",");
19979                    self.write_newline();
19980                }
19981                self.write_indent();
19982                self.generate_identifier(&named_window.name)?;
19983                self.write_space();
19984                self.write_keyword("AS");
19985                self.write(" (");
19986                self.generate_over(&named_window.spec)?;
19987                self.write(")");
19988            }
19989            self.indent_level -= 1;
19990        } else {
19991            self.write_space();
19992            self.write_keyword("WINDOW");
19993            self.write_space();
19994            for (i, named_window) in windows.iter().enumerate() {
19995                if i > 0 {
19996                    self.write(", ");
19997                }
19998                self.generate_identifier(&named_window.name)?;
19999                self.write_space();
20000                self.write_keyword("AS");
20001                self.write(" (");
20002                self.generate_over(&named_window.spec)?;
20003                self.write(")");
20004            }
20005        }
20006        Ok(())
20007    }
20008
20009    // === BATCH-GENERATED STUB METHODS (481 variants) ===
20010    fn generate_ai_agg(&mut self, e: &AIAgg) -> Result<()> {
20011        // AI_AGG(this, expression)
20012        self.write_keyword("AI_AGG");
20013        self.write("(");
20014        self.generate_expression(&e.this)?;
20015        self.write(", ");
20016        self.generate_expression(&e.expression)?;
20017        self.write(")");
20018        Ok(())
20019    }
20020
20021    fn generate_ai_classify(&mut self, e: &AIClassify) -> Result<()> {
20022        // AI_CLASSIFY(input, [categories], [config])
20023        self.write_keyword("AI_CLASSIFY");
20024        self.write("(");
20025        self.generate_expression(&e.this)?;
20026        if let Some(categories) = &e.categories {
20027            self.write(", ");
20028            self.generate_expression(categories)?;
20029        }
20030        if let Some(config) = &e.config {
20031            self.write(", ");
20032            self.generate_expression(config)?;
20033        }
20034        self.write(")");
20035        Ok(())
20036    }
20037
20038    fn generate_add_partition(&mut self, e: &AddPartition) -> Result<()> {
20039        // Python: return f"ADD {exists}{self.sql(expression.this)}{location}"
20040        self.write_keyword("ADD");
20041        self.write_space();
20042        if e.exists {
20043            self.write_keyword("IF NOT EXISTS");
20044            self.write_space();
20045        }
20046        self.generate_expression(&e.this)?;
20047        if let Some(location) = &e.location {
20048            self.write_space();
20049            self.generate_expression(location)?;
20050        }
20051        Ok(())
20052    }
20053
20054    fn generate_algorithm_property(&mut self, e: &AlgorithmProperty) -> Result<()> {
20055        // Python: return f"ALGORITHM={self.sql(expression, 'this')}"
20056        self.write_keyword("ALGORITHM");
20057        self.write("=");
20058        self.generate_expression(&e.this)?;
20059        Ok(())
20060    }
20061
20062    fn generate_aliases(&mut self, e: &Aliases) -> Result<()> {
20063        // Python: return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})"
20064        self.generate_expression(&e.this)?;
20065        self.write_space();
20066        self.write_keyword("AS");
20067        self.write(" (");
20068        for (i, expr) in e.expressions.iter().enumerate() {
20069            if i > 0 {
20070                self.write(", ");
20071            }
20072            self.generate_expression(expr)?;
20073        }
20074        self.write(")");
20075        Ok(())
20076    }
20077
20078    fn generate_allowed_values_property(&mut self, e: &AllowedValuesProperty) -> Result<()> {
20079        // Python: return f"ALLOWED_VALUES {self.expressions(e, flat=True)}"
20080        self.write_keyword("ALLOWED_VALUES");
20081        self.write_space();
20082        for (i, expr) in e.expressions.iter().enumerate() {
20083            if i > 0 {
20084                self.write(", ");
20085            }
20086            self.generate_expression(expr)?;
20087        }
20088        Ok(())
20089    }
20090
20091    fn generate_alter_column(&mut self, e: &AlterColumn) -> Result<()> {
20092        // Python: complex logic based on dtype, default, comment, visible, etc.
20093        self.write_keyword("ALTER COLUMN");
20094        self.write_space();
20095        self.generate_expression(&e.this)?;
20096
20097        if let Some(dtype) = &e.dtype {
20098            self.write_space();
20099            self.write_keyword("SET DATA TYPE");
20100            self.write_space();
20101            self.generate_expression(dtype)?;
20102            if let Some(collate) = &e.collate {
20103                self.write_space();
20104                self.write_keyword("COLLATE");
20105                self.write_space();
20106                self.generate_expression(collate)?;
20107            }
20108            if let Some(using) = &e.using {
20109                self.write_space();
20110                self.write_keyword("USING");
20111                self.write_space();
20112                self.generate_expression(using)?;
20113            }
20114        } else if let Some(default) = &e.default {
20115            self.write_space();
20116            self.write_keyword("SET DEFAULT");
20117            self.write_space();
20118            self.generate_expression(default)?;
20119        } else if let Some(comment) = &e.comment {
20120            self.write_space();
20121            self.write_keyword("COMMENT");
20122            self.write_space();
20123            self.generate_expression(comment)?;
20124        } else if let Some(drop) = &e.drop {
20125            self.write_space();
20126            self.write_keyword("DROP");
20127            self.write_space();
20128            self.generate_expression(drop)?;
20129        } else if let Some(visible) = &e.visible {
20130            self.write_space();
20131            self.generate_expression(visible)?;
20132        } else if let Some(rename_to) = &e.rename_to {
20133            self.write_space();
20134            self.write_keyword("RENAME TO");
20135            self.write_space();
20136            self.generate_expression(rename_to)?;
20137        } else if let Some(allow_null) = &e.allow_null {
20138            self.write_space();
20139            self.generate_expression(allow_null)?;
20140        }
20141        Ok(())
20142    }
20143
20144    fn generate_alter_session(&mut self, e: &AlterSession) -> Result<()> {
20145        // Python: keyword = "UNSET" if expression.args.get("unset") else "SET"; return f"{keyword} {items_sql}"
20146        self.write_keyword("ALTER SESSION");
20147        self.write_space();
20148        if e.unset.is_some() {
20149            self.write_keyword("UNSET");
20150        } else {
20151            self.write_keyword("SET");
20152        }
20153        self.write_space();
20154        for (i, expr) in e.expressions.iter().enumerate() {
20155            if i > 0 {
20156                self.write(", ");
20157            }
20158            self.generate_expression(expr)?;
20159        }
20160        Ok(())
20161    }
20162
20163    fn generate_alter_set(&mut self, e: &AlterSet) -> Result<()> {
20164        // Python (Snowflake): return f"SET{exprs}{file_format}{copy_options}{tag}"
20165        self.write_keyword("SET");
20166
20167        // Generate option (e.g., AUTHORIZATION, LOGGED, UNLOGGED, etc.)
20168        if let Some(opt) = &e.option {
20169            self.write_space();
20170            self.generate_expression(opt)?;
20171        }
20172
20173        // Generate PROPERTIES (for Trino SET PROPERTIES x = y, ...)
20174        // Check if expressions look like property assignments
20175        if !e.expressions.is_empty() {
20176            // Check if this looks like property assignments (for SET PROPERTIES)
20177            let is_properties = e.expressions.iter().any(|expr| matches!(expr, Expression::Eq(_)));
20178            if is_properties && e.option.is_none() {
20179                self.write_space();
20180                self.write_keyword("PROPERTIES");
20181            }
20182            self.write_space();
20183            for (i, expr) in e.expressions.iter().enumerate() {
20184                if i > 0 {
20185                    self.write(", ");
20186                }
20187                self.generate_expression(expr)?;
20188            }
20189        }
20190
20191        // Generate STAGE_FILE_FORMAT = (...) with space-separated properties
20192        if let Some(file_format) = &e.file_format {
20193            self.write(" ");
20194            self.write_keyword("STAGE_FILE_FORMAT");
20195            self.write(" = (");
20196            self.generate_space_separated_properties(file_format)?;
20197            self.write(")");
20198        }
20199
20200        // Generate STAGE_COPY_OPTIONS = (...) with space-separated properties
20201        if let Some(copy_options) = &e.copy_options {
20202            self.write(" ");
20203            self.write_keyword("STAGE_COPY_OPTIONS");
20204            self.write(" = (");
20205            self.generate_space_separated_properties(copy_options)?;
20206            self.write(")");
20207        }
20208
20209        // Generate TAG ...
20210        if let Some(tag) = &e.tag {
20211            self.write(" ");
20212            self.write_keyword("TAG");
20213            self.write(" ");
20214            self.generate_expression(tag)?;
20215        }
20216
20217        Ok(())
20218    }
20219
20220    /// Generate space-separated properties (for Snowflake STAGE_FILE_FORMAT, etc.)
20221    fn generate_space_separated_properties(&mut self, expr: &Expression) -> Result<()> {
20222        match expr {
20223            Expression::Tuple(t) => {
20224                for (i, prop) in t.expressions.iter().enumerate() {
20225                    if i > 0 {
20226                        self.write(" ");
20227                    }
20228                    self.generate_expression(prop)?;
20229                }
20230            }
20231            _ => {
20232                self.generate_expression(expr)?;
20233            }
20234        }
20235        Ok(())
20236    }
20237
20238    fn generate_alter_sort_key(&mut self, e: &AlterSortKey) -> Result<()> {
20239        // Python: return f"ALTER{compound} SORTKEY {this or expressions}"
20240        self.write_keyword("ALTER");
20241        if e.compound.is_some() {
20242            self.write_space();
20243            self.write_keyword("COMPOUND");
20244        }
20245        self.write_space();
20246        self.write_keyword("SORTKEY");
20247        self.write_space();
20248        if let Some(this) = &e.this {
20249            self.generate_expression(this)?;
20250        } else if !e.expressions.is_empty() {
20251            self.write("(");
20252            for (i, expr) in e.expressions.iter().enumerate() {
20253                if i > 0 {
20254                    self.write(", ");
20255                }
20256                self.generate_expression(expr)?;
20257            }
20258            self.write(")");
20259        }
20260        Ok(())
20261    }
20262
20263    fn generate_analyze(&mut self, e: &Analyze) -> Result<()> {
20264        // Python: return f"ANALYZE{options}{kind}{this}{partition}{mode}{inner_expression}{properties}"
20265        self.write_keyword("ANALYZE");
20266        if !e.options.is_empty() {
20267            self.write_space();
20268            for (i, opt) in e.options.iter().enumerate() {
20269                if i > 0 {
20270                    self.write_space();
20271                }
20272                // Write options as keywords (not identifiers) to avoid quoting reserved words like FULL
20273                if let Expression::Identifier(id) = opt {
20274                    self.write_keyword(&id.name);
20275                } else {
20276                    self.generate_expression(opt)?;
20277                }
20278            }
20279        }
20280        if let Some(kind) = &e.kind {
20281            self.write_space();
20282            self.write_keyword(kind);
20283        }
20284        if let Some(this) = &e.this {
20285            self.write_space();
20286            self.generate_expression(this)?;
20287        }
20288        // Column list: ANALYZE tbl(col1, col2) (PostgreSQL)
20289        if !e.columns.is_empty() {
20290            self.write("(");
20291            for (i, col) in e.columns.iter().enumerate() {
20292                if i > 0 {
20293                    self.write(", ");
20294                }
20295                self.write(col);
20296            }
20297            self.write(")");
20298        }
20299        if let Some(partition) = &e.partition {
20300            self.write_space();
20301            self.generate_expression(partition)?;
20302        }
20303        if let Some(mode) = &e.mode {
20304            self.write_space();
20305            self.generate_expression(mode)?;
20306        }
20307        if let Some(expression) = &e.expression {
20308            self.write_space();
20309            self.generate_expression(expression)?;
20310        }
20311        if !e.properties.is_empty() {
20312            self.write_space();
20313            self.write_keyword(self.config.with_properties_prefix);
20314            self.write(" (");
20315            for (i, prop) in e.properties.iter().enumerate() {
20316                if i > 0 {
20317                    self.write(", ");
20318                }
20319                self.generate_expression(prop)?;
20320            }
20321            self.write(")");
20322        }
20323        Ok(())
20324    }
20325
20326    fn generate_analyze_delete(&mut self, e: &AnalyzeDelete) -> Result<()> {
20327        // Python: return f"DELETE{kind} STATISTICS"
20328        self.write_keyword("DELETE");
20329        if let Some(kind) = &e.kind {
20330            self.write_space();
20331            self.write_keyword(kind);
20332        }
20333        self.write_space();
20334        self.write_keyword("STATISTICS");
20335        Ok(())
20336    }
20337
20338    fn generate_analyze_histogram(&mut self, e: &AnalyzeHistogram) -> Result<()> {
20339        // Python: return f"{this} HISTOGRAM ON {columns}{inner_expression}{update_options}"
20340        // Write `this` (UPDATE or DROP) as keyword to avoid quoting reserved words
20341        if let Expression::Identifier(id) = e.this.as_ref() {
20342            self.write_keyword(&id.name);
20343        } else {
20344            self.generate_expression(&e.this)?;
20345        }
20346        self.write_space();
20347        self.write_keyword("HISTOGRAM ON");
20348        self.write_space();
20349        for (i, expr) in e.expressions.iter().enumerate() {
20350            if i > 0 {
20351                self.write(", ");
20352            }
20353            self.generate_expression(expr)?;
20354        }
20355        if let Some(expression) = &e.expression {
20356            self.write_space();
20357            self.generate_expression(expression)?;
20358        }
20359        if let Some(update_options) = &e.update_options {
20360            self.write_space();
20361            self.generate_expression(update_options)?;
20362            self.write_space();
20363            self.write_keyword("UPDATE");
20364        }
20365        Ok(())
20366    }
20367
20368    fn generate_analyze_list_chained_rows(&mut self, e: &AnalyzeListChainedRows) -> Result<()> {
20369        // Python: return f"LIST CHAINED ROWS{inner_expression}"
20370        self.write_keyword("LIST CHAINED ROWS");
20371        if let Some(expression) = &e.expression {
20372            self.write_space();
20373            self.write_keyword("INTO");
20374            self.write_space();
20375            self.generate_expression(expression)?;
20376        }
20377        Ok(())
20378    }
20379
20380    fn generate_analyze_sample(&mut self, e: &AnalyzeSample) -> Result<()> {
20381        // Python: return f"SAMPLE {sample} {kind}"
20382        self.write_keyword("SAMPLE");
20383        self.write_space();
20384        if let Some(sample) = &e.sample {
20385            self.generate_expression(sample)?;
20386            self.write_space();
20387        }
20388        self.write_keyword(&e.kind);
20389        Ok(())
20390    }
20391
20392    fn generate_analyze_statistics(&mut self, e: &AnalyzeStatistics) -> Result<()> {
20393        // Python: return f"{kind}{option} STATISTICS{this}{columns}"
20394        self.write_keyword(&e.kind);
20395        if let Some(option) = &e.option {
20396            self.write_space();
20397            self.generate_expression(option)?;
20398        }
20399        self.write_space();
20400        self.write_keyword("STATISTICS");
20401        if let Some(this) = &e.this {
20402            self.write_space();
20403            self.generate_expression(this)?;
20404        }
20405        if !e.expressions.is_empty() {
20406            self.write_space();
20407            for (i, expr) in e.expressions.iter().enumerate() {
20408                if i > 0 {
20409                    self.write(", ");
20410                }
20411                self.generate_expression(expr)?;
20412            }
20413        }
20414        Ok(())
20415    }
20416
20417    fn generate_analyze_validate(&mut self, e: &AnalyzeValidate) -> Result<()> {
20418        // Python: return f"VALIDATE {kind}{this}{inner_expression}"
20419        self.write_keyword("VALIDATE");
20420        self.write_space();
20421        self.write_keyword(&e.kind);
20422        if let Some(this) = &e.this {
20423            self.write_space();
20424            // this is a keyword string like "UPDATE", "CASCADE FAST", etc. - write as keywords
20425            if let Expression::Identifier(id) = this.as_ref() {
20426                self.write_keyword(&id.name);
20427            } else {
20428                self.generate_expression(this)?;
20429            }
20430        }
20431        if let Some(expression) = &e.expression {
20432            self.write_space();
20433            self.write_keyword("INTO");
20434            self.write_space();
20435            self.generate_expression(expression)?;
20436        }
20437        Ok(())
20438    }
20439
20440    fn generate_analyze_with(&mut self, e: &AnalyzeWith) -> Result<()> {
20441        // Python: return f"WITH {expressions}"
20442        self.write_keyword("WITH");
20443        self.write_space();
20444        for (i, expr) in e.expressions.iter().enumerate() {
20445            if i > 0 {
20446                self.write(", ");
20447            }
20448            self.generate_expression(expr)?;
20449        }
20450        Ok(())
20451    }
20452
20453    fn generate_anonymous(&mut self, e: &Anonymous) -> Result<()> {
20454        // Anonymous represents a generic function call: FUNC_NAME(args...)
20455        // Python: return self.func(self.sql(expression, "this"), *expression.expressions)
20456        self.generate_expression(&e.this)?;
20457        self.write("(");
20458        for (i, arg) in e.expressions.iter().enumerate() {
20459            if i > 0 {
20460                self.write(", ");
20461            }
20462            self.generate_expression(arg)?;
20463        }
20464        self.write(")");
20465        Ok(())
20466    }
20467
20468    fn generate_anonymous_agg_func(&mut self, e: &AnonymousAggFunc) -> Result<()> {
20469        // Same as Anonymous but for aggregate functions
20470        self.generate_expression(&e.this)?;
20471        self.write("(");
20472        for (i, arg) in e.expressions.iter().enumerate() {
20473            if i > 0 {
20474                self.write(", ");
20475            }
20476            self.generate_expression(arg)?;
20477        }
20478        self.write(")");
20479        Ok(())
20480    }
20481
20482    fn generate_apply(&mut self, e: &Apply) -> Result<()> {
20483        // Python: return f"{this} APPLY({expr})"
20484        self.generate_expression(&e.this)?;
20485        self.write_space();
20486        self.write_keyword("APPLY");
20487        self.write("(");
20488        self.generate_expression(&e.expression)?;
20489        self.write(")");
20490        Ok(())
20491    }
20492
20493    fn generate_approx_percentile_estimate(&mut self, e: &ApproxPercentileEstimate) -> Result<()> {
20494        // APPROX_PERCENTILE_ESTIMATE(this, percentile)
20495        self.write_keyword("APPROX_PERCENTILE_ESTIMATE");
20496        self.write("(");
20497        self.generate_expression(&e.this)?;
20498        if let Some(percentile) = &e.percentile {
20499            self.write(", ");
20500            self.generate_expression(percentile)?;
20501        }
20502        self.write(")");
20503        Ok(())
20504    }
20505
20506    fn generate_approx_quantile(&mut self, e: &ApproxQuantile) -> Result<()> {
20507        // APPROX_QUANTILE(this, quantile[, accuracy][, weight])
20508        self.write_keyword("APPROX_QUANTILE");
20509        self.write("(");
20510        self.generate_expression(&e.this)?;
20511        if let Some(quantile) = &e.quantile {
20512            self.write(", ");
20513            self.generate_expression(quantile)?;
20514        }
20515        if let Some(accuracy) = &e.accuracy {
20516            self.write(", ");
20517            self.generate_expression(accuracy)?;
20518        }
20519        if let Some(weight) = &e.weight {
20520            self.write(", ");
20521            self.generate_expression(weight)?;
20522        }
20523        self.write(")");
20524        Ok(())
20525    }
20526
20527    fn generate_approx_quantiles(&mut self, e: &ApproxQuantiles) -> Result<()> {
20528        // APPROX_QUANTILES(this, expression)
20529        self.write_keyword("APPROX_QUANTILES");
20530        self.write("(");
20531        self.generate_expression(&e.this)?;
20532        if let Some(expression) = &e.expression {
20533            self.write(", ");
20534            self.generate_expression(expression)?;
20535        }
20536        self.write(")");
20537        Ok(())
20538    }
20539
20540    fn generate_approx_top_k(&mut self, e: &ApproxTopK) -> Result<()> {
20541        // APPROX_TOP_K(this[, expression][, counters])
20542        self.write_keyword("APPROX_TOP_K");
20543        self.write("(");
20544        self.generate_expression(&e.this)?;
20545        if let Some(expression) = &e.expression {
20546            self.write(", ");
20547            self.generate_expression(expression)?;
20548        }
20549        if let Some(counters) = &e.counters {
20550            self.write(", ");
20551            self.generate_expression(counters)?;
20552        }
20553        self.write(")");
20554        Ok(())
20555    }
20556
20557    fn generate_approx_top_k_accumulate(&mut self, e: &ApproxTopKAccumulate) -> Result<()> {
20558        // APPROX_TOP_K_ACCUMULATE(this[, expression])
20559        self.write_keyword("APPROX_TOP_K_ACCUMULATE");
20560        self.write("(");
20561        self.generate_expression(&e.this)?;
20562        if let Some(expression) = &e.expression {
20563            self.write(", ");
20564            self.generate_expression(expression)?;
20565        }
20566        self.write(")");
20567        Ok(())
20568    }
20569
20570    fn generate_approx_top_k_combine(&mut self, e: &ApproxTopKCombine) -> Result<()> {
20571        // APPROX_TOP_K_COMBINE(this[, expression])
20572        self.write_keyword("APPROX_TOP_K_COMBINE");
20573        self.write("(");
20574        self.generate_expression(&e.this)?;
20575        if let Some(expression) = &e.expression {
20576            self.write(", ");
20577            self.generate_expression(expression)?;
20578        }
20579        self.write(")");
20580        Ok(())
20581    }
20582
20583    fn generate_approx_top_k_estimate(&mut self, e: &ApproxTopKEstimate) -> Result<()> {
20584        // APPROX_TOP_K_ESTIMATE(this[, expression])
20585        self.write_keyword("APPROX_TOP_K_ESTIMATE");
20586        self.write("(");
20587        self.generate_expression(&e.this)?;
20588        if let Some(expression) = &e.expression {
20589            self.write(", ");
20590            self.generate_expression(expression)?;
20591        }
20592        self.write(")");
20593        Ok(())
20594    }
20595
20596    fn generate_approx_top_sum(&mut self, e: &ApproxTopSum) -> Result<()> {
20597        // APPROX_TOP_SUM(this, expression[, count])
20598        self.write_keyword("APPROX_TOP_SUM");
20599        self.write("(");
20600        self.generate_expression(&e.this)?;
20601        self.write(", ");
20602        self.generate_expression(&e.expression)?;
20603        if let Some(count) = &e.count {
20604            self.write(", ");
20605            self.generate_expression(count)?;
20606        }
20607        self.write(")");
20608        Ok(())
20609    }
20610
20611    fn generate_arg_max(&mut self, e: &ArgMax) -> Result<()> {
20612        // ARG_MAX(this, expression[, count])
20613        self.write_keyword("ARG_MAX");
20614        self.write("(");
20615        self.generate_expression(&e.this)?;
20616        self.write(", ");
20617        self.generate_expression(&e.expression)?;
20618        if let Some(count) = &e.count {
20619            self.write(", ");
20620            self.generate_expression(count)?;
20621        }
20622        self.write(")");
20623        Ok(())
20624    }
20625
20626    fn generate_arg_min(&mut self, e: &ArgMin) -> Result<()> {
20627        // ARG_MIN(this, expression[, count])
20628        self.write_keyword("ARG_MIN");
20629        self.write("(");
20630        self.generate_expression(&e.this)?;
20631        self.write(", ");
20632        self.generate_expression(&e.expression)?;
20633        if let Some(count) = &e.count {
20634            self.write(", ");
20635            self.generate_expression(count)?;
20636        }
20637        self.write(")");
20638        Ok(())
20639    }
20640
20641    fn generate_array_all(&mut self, e: &ArrayAll) -> Result<()> {
20642        // ARRAY_ALL(this, expression)
20643        self.write_keyword("ARRAY_ALL");
20644        self.write("(");
20645        self.generate_expression(&e.this)?;
20646        self.write(", ");
20647        self.generate_expression(&e.expression)?;
20648        self.write(")");
20649        Ok(())
20650    }
20651
20652    fn generate_array_any(&mut self, e: &ArrayAny) -> Result<()> {
20653        // ARRAY_ANY(this, expression) - fallback implementation
20654        self.write_keyword("ARRAY_ANY");
20655        self.write("(");
20656        self.generate_expression(&e.this)?;
20657        self.write(", ");
20658        self.generate_expression(&e.expression)?;
20659        self.write(")");
20660        Ok(())
20661    }
20662
20663    fn generate_array_construct_compact(&mut self, e: &ArrayConstructCompact) -> Result<()> {
20664        // ARRAY_CONSTRUCT_COMPACT(expressions...)
20665        self.write_keyword("ARRAY_CONSTRUCT_COMPACT");
20666        self.write("(");
20667        for (i, expr) in e.expressions.iter().enumerate() {
20668            if i > 0 {
20669                self.write(", ");
20670            }
20671            self.generate_expression(expr)?;
20672        }
20673        self.write(")");
20674        Ok(())
20675    }
20676
20677    fn generate_array_sum(&mut self, e: &ArraySum) -> Result<()> {
20678        // ARRAY_SUM(this[, expression])
20679        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
20680            self.write("arraySum");
20681        } else {
20682            self.write_keyword("ARRAY_SUM");
20683        }
20684        self.write("(");
20685        self.generate_expression(&e.this)?;
20686        if let Some(expression) = &e.expression {
20687            self.write(", ");
20688            self.generate_expression(expression)?;
20689        }
20690        self.write(")");
20691        Ok(())
20692    }
20693
20694    fn generate_at_index(&mut self, e: &AtIndex) -> Result<()> {
20695        // Python: return f"{this} AT {index}"
20696        self.generate_expression(&e.this)?;
20697        self.write_space();
20698        self.write_keyword("AT");
20699        self.write_space();
20700        self.generate_expression(&e.expression)?;
20701        Ok(())
20702    }
20703
20704    fn generate_attach(&mut self, e: &Attach) -> Result<()> {
20705        // Python: return f"ATTACH{exists_sql} {this}{expressions}"
20706        self.write_keyword("ATTACH");
20707        if e.exists {
20708            self.write_space();
20709            self.write_keyword("IF NOT EXISTS");
20710        }
20711        self.write_space();
20712        self.generate_expression(&e.this)?;
20713        if !e.expressions.is_empty() {
20714            self.write(" (");
20715            for (i, expr) in e.expressions.iter().enumerate() {
20716                if i > 0 {
20717                    self.write(", ");
20718                }
20719                self.generate_expression(expr)?;
20720            }
20721            self.write(")");
20722        }
20723        Ok(())
20724    }
20725
20726    fn generate_attach_option(&mut self, e: &AttachOption) -> Result<()> {
20727        // AttachOption: this [expression]
20728        // Python sqlglot: no equals sign, just space-separated
20729        self.generate_expression(&e.this)?;
20730        if let Some(expression) = &e.expression {
20731            self.write_space();
20732            self.generate_expression(expression)?;
20733        }
20734        Ok(())
20735    }
20736
20737    /// Generate the auto_increment keyword and options for a column definition.
20738    /// Different dialects use different syntax: IDENTITY, AUTOINCREMENT, AUTO_INCREMENT,
20739    /// GENERATED AS IDENTITY, etc.
20740    fn generate_auto_increment_keyword(&mut self, col: &crate::expressions::ColumnDef) -> Result<()> {
20741        use crate::dialects::DialectType;
20742        if matches!(self.config.dialect, Some(DialectType::Redshift)) {
20743            self.write_keyword("IDENTITY");
20744            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20745                self.write("(");
20746                if let Some(ref start) = col.auto_increment_start {
20747                    self.generate_expression(start)?;
20748                } else {
20749                    self.write("0");
20750                }
20751                self.write(", ");
20752                if let Some(ref inc) = col.auto_increment_increment {
20753                    self.generate_expression(inc)?;
20754                } else {
20755                    self.write("1");
20756                }
20757                self.write(")");
20758            }
20759        } else if matches!(self.config.dialect, Some(DialectType::Snowflake) | Some(DialectType::SQLite)) {
20760            self.write_keyword("AUTOINCREMENT");
20761            if let Some(ref start) = col.auto_increment_start {
20762                self.write_space();
20763                self.write_keyword("START");
20764                self.write_space();
20765                self.generate_expression(start)?;
20766            }
20767            if let Some(ref inc) = col.auto_increment_increment {
20768                self.write_space();
20769                self.write_keyword("INCREMENT");
20770                self.write_space();
20771                self.generate_expression(inc)?;
20772            }
20773            if let Some(order) = col.auto_increment_order {
20774                self.write_space();
20775                if order {
20776                    self.write_keyword("ORDER");
20777                } else {
20778                    self.write_keyword("NOORDER");
20779                }
20780            }
20781        } else if matches!(self.config.dialect, Some(DialectType::PostgreSQL)) {
20782            self.write_keyword("GENERATED BY DEFAULT AS IDENTITY");
20783            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20784                self.write(" (");
20785                let mut first = true;
20786                if let Some(ref start) = col.auto_increment_start {
20787                    self.write_keyword("START WITH");
20788                    self.write_space();
20789                    self.generate_expression(start)?;
20790                    first = false;
20791                }
20792                if let Some(ref inc) = col.auto_increment_increment {
20793                    if !first { self.write_space(); }
20794                    self.write_keyword("INCREMENT BY");
20795                    self.write_space();
20796                    self.generate_expression(inc)?;
20797                }
20798                self.write(")");
20799            }
20800        } else if matches!(self.config.dialect, Some(DialectType::Databricks)) {
20801            self.write_keyword("GENERATED ALWAYS AS IDENTITY");
20802            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20803                self.write(" (");
20804                let mut first = true;
20805                if let Some(ref start) = col.auto_increment_start {
20806                    self.write_keyword("START WITH");
20807                    self.write_space();
20808                    self.generate_expression(start)?;
20809                    first = false;
20810                }
20811                if let Some(ref inc) = col.auto_increment_increment {
20812                    if !first { self.write_space(); }
20813                    self.write_keyword("INCREMENT BY");
20814                    self.write_space();
20815                    self.generate_expression(inc)?;
20816                }
20817                self.write(")");
20818            }
20819        } else if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
20820            self.write_keyword("IDENTITY");
20821            if col.auto_increment_start.is_some() || col.auto_increment_increment.is_some() {
20822                self.write("(");
20823                if let Some(ref start) = col.auto_increment_start {
20824                    self.generate_expression(start)?;
20825                } else {
20826                    self.write("0");
20827                }
20828                self.write(", ");
20829                if let Some(ref inc) = col.auto_increment_increment {
20830                    self.generate_expression(inc)?;
20831                } else {
20832                    self.write("1");
20833                }
20834                self.write(")");
20835            }
20836        } else {
20837            self.write_keyword("AUTO_INCREMENT");
20838            if let Some(ref start) = col.auto_increment_start {
20839                self.write_space();
20840                self.write_keyword("START");
20841                self.write_space();
20842                self.generate_expression(start)?;
20843            }
20844            if let Some(ref inc) = col.auto_increment_increment {
20845                self.write_space();
20846                self.write_keyword("INCREMENT");
20847                self.write_space();
20848                self.generate_expression(inc)?;
20849            }
20850            if let Some(order) = col.auto_increment_order {
20851                self.write_space();
20852                if order {
20853                    self.write_keyword("ORDER");
20854                } else {
20855                    self.write_keyword("NOORDER");
20856                }
20857            }
20858        }
20859        Ok(())
20860    }
20861
20862    fn generate_auto_increment_property(&mut self, e: &AutoIncrementProperty) -> Result<()> {
20863        // AUTO_INCREMENT=value
20864        self.write_keyword("AUTO_INCREMENT");
20865        self.write("=");
20866        self.generate_expression(&e.this)?;
20867        Ok(())
20868    }
20869
20870    fn generate_auto_refresh_property(&mut self, e: &AutoRefreshProperty) -> Result<()> {
20871        // AUTO_REFRESH=value
20872        self.write_keyword("AUTO_REFRESH");
20873        self.write("=");
20874        self.generate_expression(&e.this)?;
20875        Ok(())
20876    }
20877
20878    fn generate_backup_property(&mut self, e: &BackupProperty) -> Result<()> {
20879        // BACKUP YES|NO (Redshift syntax uses space, not equals)
20880        self.write_keyword("BACKUP");
20881        self.write_space();
20882        self.generate_expression(&e.this)?;
20883        Ok(())
20884    }
20885
20886    fn generate_base64_decode_binary(&mut self, e: &Base64DecodeBinary) -> Result<()> {
20887        // BASE64_DECODE_BINARY(this[, alphabet])
20888        self.write_keyword("BASE64_DECODE_BINARY");
20889        self.write("(");
20890        self.generate_expression(&e.this)?;
20891        if let Some(alphabet) = &e.alphabet {
20892            self.write(", ");
20893            self.generate_expression(alphabet)?;
20894        }
20895        self.write(")");
20896        Ok(())
20897    }
20898
20899    fn generate_base64_decode_string(&mut self, e: &Base64DecodeString) -> Result<()> {
20900        // BASE64_DECODE_STRING(this[, alphabet])
20901        self.write_keyword("BASE64_DECODE_STRING");
20902        self.write("(");
20903        self.generate_expression(&e.this)?;
20904        if let Some(alphabet) = &e.alphabet {
20905            self.write(", ");
20906            self.generate_expression(alphabet)?;
20907        }
20908        self.write(")");
20909        Ok(())
20910    }
20911
20912    fn generate_base64_encode(&mut self, e: &Base64Encode) -> Result<()> {
20913        // BASE64_ENCODE(this[, max_line_length][, alphabet])
20914        self.write_keyword("BASE64_ENCODE");
20915        self.write("(");
20916        self.generate_expression(&e.this)?;
20917        if let Some(max_line_length) = &e.max_line_length {
20918            self.write(", ");
20919            self.generate_expression(max_line_length)?;
20920        }
20921        if let Some(alphabet) = &e.alphabet {
20922            self.write(", ");
20923            self.generate_expression(alphabet)?;
20924        }
20925        self.write(")");
20926        Ok(())
20927    }
20928
20929    fn generate_block_compression_property(&mut self, e: &BlockCompressionProperty) -> Result<()> {
20930        // BLOCKCOMPRESSION=... (complex Teradata property)
20931        self.write_keyword("BLOCKCOMPRESSION");
20932        self.write("=");
20933        if let Some(autotemp) = &e.autotemp {
20934            self.write_keyword("AUTOTEMP");
20935            self.write("(");
20936            self.generate_expression(autotemp)?;
20937            self.write(")");
20938        }
20939        if let Some(always) = &e.always {
20940            self.generate_expression(always)?;
20941        }
20942        if let Some(default) = &e.default {
20943            self.generate_expression(default)?;
20944        }
20945        if let Some(manual) = &e.manual {
20946            self.generate_expression(manual)?;
20947        }
20948        if let Some(never) = &e.never {
20949            self.generate_expression(never)?;
20950        }
20951        Ok(())
20952    }
20953
20954    fn generate_booland(&mut self, e: &Booland) -> Result<()> {
20955        // Python: return f"(({self.sql(expression, 'this')}) AND ({self.sql(expression, 'expression')}))"
20956        self.write("((");
20957        self.generate_expression(&e.this)?;
20958        self.write(") ");
20959        self.write_keyword("AND");
20960        self.write(" (");
20961        self.generate_expression(&e.expression)?;
20962        self.write("))");
20963        Ok(())
20964    }
20965
20966    fn generate_boolor(&mut self, e: &Boolor) -> Result<()> {
20967        // Python: return f"(({self.sql(expression, 'this')}) OR ({self.sql(expression, 'expression')}))"
20968        self.write("((");
20969        self.generate_expression(&e.this)?;
20970        self.write(") ");
20971        self.write_keyword("OR");
20972        self.write(" (");
20973        self.generate_expression(&e.expression)?;
20974        self.write("))");
20975        Ok(())
20976    }
20977
20978    fn generate_build_property(&mut self, e: &BuildProperty) -> Result<()> {
20979        // BUILD value (e.g., BUILD IMMEDIATE, BUILD DEFERRED)
20980        self.write_keyword("BUILD");
20981        self.write_space();
20982        self.generate_expression(&e.this)?;
20983        Ok(())
20984    }
20985
20986    fn generate_byte_string(&mut self, e: &ByteString) -> Result<()> {
20987        // Byte string literal like B'...' or X'...'
20988        self.generate_expression(&e.this)?;
20989        Ok(())
20990    }
20991
20992    fn generate_case_specific_column_constraint(&mut self, e: &CaseSpecificColumnConstraint) -> Result<()> {
20993        // CASESPECIFIC or NOT CASESPECIFIC (Teradata)
20994        if e.not_.is_some() {
20995            self.write_keyword("NOT");
20996            self.write_space();
20997        }
20998        self.write_keyword("CASESPECIFIC");
20999        Ok(())
21000    }
21001
21002    fn generate_cast_to_str_type(&mut self, e: &CastToStrType) -> Result<()> {
21003        // Cast to string type (dialect-specific)
21004        self.write_keyword("CAST");
21005        self.write("(");
21006        self.generate_expression(&e.this)?;
21007        if self.config.dialect == Some(DialectType::ClickHouse) {
21008            // ClickHouse: CAST(expr, 'type_string')
21009            self.write(", ");
21010        } else {
21011            self.write_space();
21012            self.write_keyword("AS");
21013            self.write_space();
21014        }
21015        if let Some(to) = &e.to {
21016            self.generate_expression(to)?;
21017        }
21018        self.write(")");
21019        Ok(())
21020    }
21021
21022    fn generate_changes(&mut self, e: &Changes) -> Result<()> {
21023        // CHANGES (INFORMATION => value) AT|BEFORE (...) END (...)
21024        // Python: f"CHANGES ({information}){at_before}{end}"
21025        self.write_keyword("CHANGES");
21026        self.write(" (");
21027        if let Some(information) = &e.information {
21028            self.write_keyword("INFORMATION");
21029            self.write(" => ");
21030            self.generate_expression(information)?;
21031        }
21032        self.write(")");
21033        // at_before and end are HistoricalData expressions that generate their own keywords
21034        if let Some(at_before) = &e.at_before {
21035            self.write(" ");
21036            self.generate_expression(at_before)?;
21037        }
21038        if let Some(end) = &e.end {
21039            self.write(" ");
21040            self.generate_expression(end)?;
21041        }
21042        Ok(())
21043    }
21044
21045    fn generate_character_set_column_constraint(&mut self, e: &CharacterSetColumnConstraint) -> Result<()> {
21046        // CHARACTER SET charset_name
21047        self.write_keyword("CHARACTER SET");
21048        self.write_space();
21049        self.generate_expression(&e.this)?;
21050        Ok(())
21051    }
21052
21053    fn generate_character_set_property(&mut self, e: &CharacterSetProperty) -> Result<()> {
21054        // [DEFAULT] CHARACTER SET=value
21055        if e.default.is_some() {
21056            self.write_keyword("DEFAULT");
21057            self.write_space();
21058        }
21059        self.write_keyword("CHARACTER SET");
21060        self.write("=");
21061        self.generate_expression(&e.this)?;
21062        Ok(())
21063    }
21064
21065    fn generate_check_column_constraint(&mut self, e: &CheckColumnConstraint) -> Result<()> {
21066        // Python: return f"CHECK ({self.sql(expression, 'this')}){enforced}"
21067        self.write_keyword("CHECK");
21068        self.write(" (");
21069        self.generate_expression(&e.this)?;
21070        self.write(")");
21071        if e.enforced.is_some() {
21072            self.write_space();
21073            self.write_keyword("ENFORCED");
21074        }
21075        Ok(())
21076    }
21077
21078    fn generate_check_json(&mut self, e: &CheckJson) -> Result<()> {
21079        // CHECK_JSON(this)
21080        self.write_keyword("CHECK_JSON");
21081        self.write("(");
21082        self.generate_expression(&e.this)?;
21083        self.write(")");
21084        Ok(())
21085    }
21086
21087    fn generate_check_xml(&mut self, e: &CheckXml) -> Result<()> {
21088        // CHECK_XML(this)
21089        self.write_keyword("CHECK_XML");
21090        self.write("(");
21091        self.generate_expression(&e.this)?;
21092        self.write(")");
21093        Ok(())
21094    }
21095
21096    fn generate_checksum_property(&mut self, e: &ChecksumProperty) -> Result<()> {
21097        // CHECKSUM=[ON|OFF|DEFAULT]
21098        self.write_keyword("CHECKSUM");
21099        self.write("=");
21100        if e.on.is_some() {
21101            self.write_keyword("ON");
21102        } else if e.default.is_some() {
21103            self.write_keyword("DEFAULT");
21104        } else {
21105            self.write_keyword("OFF");
21106        }
21107        Ok(())
21108    }
21109
21110    fn generate_clone(&mut self, e: &Clone) -> Result<()> {
21111        // Python: return f"{shallow}{keyword} {this}"
21112        if e.shallow.is_some() {
21113            self.write_keyword("SHALLOW");
21114            self.write_space();
21115        }
21116        if e.copy.is_some() {
21117            self.write_keyword("COPY");
21118        } else {
21119            self.write_keyword("CLONE");
21120        }
21121        self.write_space();
21122        self.generate_expression(&e.this)?;
21123        Ok(())
21124    }
21125
21126    fn generate_cluster_by(&mut self, e: &ClusterBy) -> Result<()> {
21127        // CLUSTER BY (expressions)
21128        self.write_keyword("CLUSTER BY");
21129        self.write(" (");
21130        for (i, ord) in e.expressions.iter().enumerate() {
21131            if i > 0 {
21132                self.write(", ");
21133            }
21134            self.generate_ordered(ord)?;
21135        }
21136        self.write(")");
21137        Ok(())
21138    }
21139
21140    fn generate_clustered_by_property(&mut self, e: &ClusteredByProperty) -> Result<()> {
21141        // Python: return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
21142        self.write_keyword("CLUSTERED BY");
21143        self.write(" (");
21144        for (i, expr) in e.expressions.iter().enumerate() {
21145            if i > 0 {
21146                self.write(", ");
21147            }
21148            self.generate_expression(expr)?;
21149        }
21150        self.write(")");
21151        if let Some(sorted_by) = &e.sorted_by {
21152            self.write_space();
21153            self.write_keyword("SORTED BY");
21154            self.write(" (");
21155            // Unwrap Tuple to avoid double parentheses
21156            if let Expression::Tuple(t) = sorted_by.as_ref() {
21157                for (i, expr) in t.expressions.iter().enumerate() {
21158                    if i > 0 {
21159                        self.write(", ");
21160                    }
21161                    self.generate_expression(expr)?;
21162                }
21163            } else {
21164                self.generate_expression(sorted_by)?;
21165            }
21166            self.write(")");
21167        }
21168        if let Some(buckets) = &e.buckets {
21169            self.write_space();
21170            self.write_keyword("INTO");
21171            self.write_space();
21172            self.generate_expression(buckets)?;
21173            self.write_space();
21174            self.write_keyword("BUCKETS");
21175        }
21176        Ok(())
21177    }
21178
21179    fn generate_collate_property(&mut self, e: &CollateProperty) -> Result<()> {
21180        // [DEFAULT] COLLATE [=] value
21181        // BigQuery uses space: DEFAULT COLLATE 'en'
21182        // Others use equals: COLLATE='en'
21183        if e.default.is_some() {
21184            self.write_keyword("DEFAULT");
21185            self.write_space();
21186        }
21187        self.write_keyword("COLLATE");
21188        // BigQuery uses space between COLLATE and value
21189        match self.config.dialect {
21190            Some(DialectType::BigQuery) => self.write_space(),
21191            _ => self.write("="),
21192        }
21193        self.generate_expression(&e.this)?;
21194        Ok(())
21195    }
21196
21197    fn generate_column_constraint(&mut self, e: &ColumnConstraint) -> Result<()> {
21198        // ColumnConstraint is an enum
21199        match e {
21200            ColumnConstraint::NotNull => {
21201                self.write_keyword("NOT NULL");
21202            }
21203            ColumnConstraint::Null => {
21204                self.write_keyword("NULL");
21205            }
21206            ColumnConstraint::Unique => {
21207                self.write_keyword("UNIQUE");
21208            }
21209            ColumnConstraint::PrimaryKey => {
21210                self.write_keyword("PRIMARY KEY");
21211            }
21212            ColumnConstraint::Default(expr) => {
21213                self.write_keyword("DEFAULT");
21214                self.write_space();
21215                self.generate_expression(expr)?;
21216            }
21217            ColumnConstraint::Check(expr) => {
21218                self.write_keyword("CHECK");
21219                self.write(" (");
21220                self.generate_expression(expr)?;
21221                self.write(")");
21222            }
21223            ColumnConstraint::References(fk_ref) => {
21224                if fk_ref.has_foreign_key_keywords {
21225                    self.write_keyword("FOREIGN KEY");
21226                    self.write_space();
21227                }
21228                self.write_keyword("REFERENCES");
21229                self.write_space();
21230                self.generate_table(&fk_ref.table)?;
21231                if !fk_ref.columns.is_empty() {
21232                    self.write(" (");
21233                    for (i, col) in fk_ref.columns.iter().enumerate() {
21234                        if i > 0 {
21235                            self.write(", ");
21236                        }
21237                        self.generate_identifier(col)?;
21238                    }
21239                    self.write(")");
21240                }
21241            }
21242            ColumnConstraint::GeneratedAsIdentity(gen) => {
21243                self.write_keyword("GENERATED");
21244                self.write_space();
21245                if gen.always {
21246                    self.write_keyword("ALWAYS");
21247                } else {
21248                    self.write_keyword("BY DEFAULT");
21249                    if gen.on_null {
21250                        self.write_space();
21251                        self.write_keyword("ON NULL");
21252                    }
21253                }
21254                self.write_space();
21255                self.write_keyword("AS IDENTITY");
21256            }
21257            ColumnConstraint::Collate(collation) => {
21258                self.write_keyword("COLLATE");
21259                self.write_space();
21260                self.generate_identifier(collation)?;
21261            }
21262            ColumnConstraint::Comment(comment) => {
21263                self.write_keyword("COMMENT");
21264                self.write(" '");
21265                self.write(comment);
21266                self.write("'");
21267            }
21268            ColumnConstraint::ComputedColumn(cc) => {
21269                self.generate_computed_column_inline(cc)?;
21270            }
21271            ColumnConstraint::GeneratedAsRow(gar) => {
21272                self.generate_generated_as_row_inline(gar)?;
21273            }
21274            ColumnConstraint::Tags(tags) => {
21275                self.write_keyword("TAG");
21276                self.write(" (");
21277                for (i, expr) in tags.expressions.iter().enumerate() {
21278                    if i > 0 {
21279                        self.write(", ");
21280                    }
21281                    self.generate_expression(expr)?;
21282                }
21283                self.write(")");
21284            }
21285            ColumnConstraint::Path(path_expr) => {
21286                self.write_keyword("PATH");
21287                self.write_space();
21288                self.generate_expression(path_expr)?;
21289            }
21290        }
21291        Ok(())
21292    }
21293
21294    fn generate_column_position(&mut self, e: &ColumnPosition) -> Result<()> {
21295        // ColumnPosition is an enum
21296        match e {
21297            ColumnPosition::First => {
21298                self.write_keyword("FIRST");
21299            }
21300            ColumnPosition::After(ident) => {
21301                self.write_keyword("AFTER");
21302                self.write_space();
21303                self.generate_identifier(ident)?;
21304            }
21305        }
21306        Ok(())
21307    }
21308
21309    fn generate_column_prefix(&mut self, e: &ColumnPrefix) -> Result<()> {
21310        // column(prefix)
21311        self.generate_expression(&e.this)?;
21312        self.write("(");
21313        self.generate_expression(&e.expression)?;
21314        self.write(")");
21315        Ok(())
21316    }
21317
21318    fn generate_columns(&mut self, e: &Columns) -> Result<()> {
21319        // If unpack is true, this came from * COLUMNS(pattern)
21320        // DuckDB syntax: * COLUMNS(c ILIKE '%suffix') or COLUMNS(pattern)
21321        if let Some(ref unpack) = e.unpack {
21322            if let Expression::Boolean(b) = unpack.as_ref() {
21323                if b.value {
21324                    self.write("*");
21325                }
21326            }
21327        }
21328        self.write_keyword("COLUMNS");
21329        self.write("(");
21330        self.generate_expression(&e.this)?;
21331        self.write(")");
21332        Ok(())
21333    }
21334
21335    fn generate_combined_agg_func(&mut self, e: &CombinedAggFunc) -> Result<()> {
21336        // Combined aggregate: FUNC(args) combined
21337        self.generate_expression(&e.this)?;
21338        self.write("(");
21339        for (i, expr) in e.expressions.iter().enumerate() {
21340            if i > 0 {
21341                self.write(", ");
21342            }
21343            self.generate_expression(expr)?;
21344        }
21345        self.write(")");
21346        Ok(())
21347    }
21348
21349    fn generate_combined_parameterized_agg(&mut self, e: &CombinedParameterizedAgg) -> Result<()> {
21350        // Combined parameterized aggregate: FUNC(params)(expressions)
21351        self.generate_expression(&e.this)?;
21352        self.write("(");
21353        for (i, param) in e.params.iter().enumerate() {
21354            if i > 0 {
21355                self.write(", ");
21356            }
21357            self.generate_expression(param)?;
21358        }
21359        self.write(")(");
21360        for (i, expr) in e.expressions.iter().enumerate() {
21361            if i > 0 {
21362                self.write(", ");
21363            }
21364            self.generate_expression(expr)?;
21365        }
21366        self.write(")");
21367        Ok(())
21368    }
21369
21370    fn generate_commit(&mut self, e: &Commit) -> Result<()> {
21371        // COMMIT [TRANSACTION [transaction_name]] [WITH (DELAYED_DURABILITY = ON|OFF)] [AND [NO] CHAIN]
21372        self.write_keyword("COMMIT");
21373
21374        // TSQL always uses COMMIT TRANSACTION
21375        if e.this.is_none() && matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21376            self.write_space();
21377            self.write_keyword("TRANSACTION");
21378        }
21379
21380        // Check if this has TRANSACTION keyword or transaction name
21381        if let Some(this) = &e.this {
21382            // Check if it's just the "TRANSACTION" marker or an actual transaction name
21383            let is_transaction_marker = matches!(
21384                this.as_ref(),
21385                Expression::Identifier(id) if id.name == "TRANSACTION"
21386            );
21387
21388            self.write_space();
21389            self.write_keyword("TRANSACTION");
21390
21391            // If it's a real transaction name, output it
21392            if !is_transaction_marker {
21393                self.write_space();
21394                self.generate_expression(this)?;
21395            }
21396        }
21397
21398        // Output WITH (DELAYED_DURABILITY = ON|OFF) for TSQL
21399        if let Some(durability) = &e.durability {
21400            self.write_space();
21401            self.write_keyword("WITH");
21402            self.write(" (");
21403            self.write_keyword("DELAYED_DURABILITY");
21404            self.write(" = ");
21405            if let Expression::Boolean(BooleanLiteral { value: true }) = durability.as_ref() {
21406                self.write_keyword("ON");
21407            } else {
21408                self.write_keyword("OFF");
21409            }
21410            self.write(")");
21411        }
21412
21413        // Output AND [NO] CHAIN
21414        if let Some(chain) = &e.chain {
21415            self.write_space();
21416            if let Expression::Boolean(BooleanLiteral { value: false }) = chain.as_ref() {
21417                self.write_keyword("AND NO CHAIN");
21418            } else {
21419                self.write_keyword("AND CHAIN");
21420            }
21421        }
21422        Ok(())
21423    }
21424
21425    fn generate_comprehension(&mut self, e: &Comprehension) -> Result<()> {
21426        // Python-style comprehension: [expr FOR var[, pos] IN iterator IF condition]
21427        self.write("[");
21428        self.generate_expression(&e.this)?;
21429        self.write_space();
21430        self.write_keyword("FOR");
21431        self.write_space();
21432        self.generate_expression(&e.expression)?;
21433        // Handle optional position variable (for enumerate-like syntax)
21434        if let Some(pos) = &e.position {
21435            self.write(", ");
21436            self.generate_expression(pos)?;
21437        }
21438        if let Some(iterator) = &e.iterator {
21439            self.write_space();
21440            self.write_keyword("IN");
21441            self.write_space();
21442            self.generate_expression(iterator)?;
21443        }
21444        if let Some(condition) = &e.condition {
21445            self.write_space();
21446            self.write_keyword("IF");
21447            self.write_space();
21448            self.generate_expression(condition)?;
21449        }
21450        self.write("]");
21451        Ok(())
21452    }
21453
21454    fn generate_compress(&mut self, e: &Compress) -> Result<()> {
21455        // COMPRESS(this[, method])
21456        self.write_keyword("COMPRESS");
21457        self.write("(");
21458        self.generate_expression(&e.this)?;
21459        if let Some(method) = &e.method {
21460            self.write(", '");
21461            self.write(method);
21462            self.write("'");
21463        }
21464        self.write(")");
21465        Ok(())
21466    }
21467
21468    fn generate_compress_column_constraint(&mut self, e: &CompressColumnConstraint) -> Result<()> {
21469        // Python: return f"COMPRESS {this}"
21470        self.write_keyword("COMPRESS");
21471        if let Some(this) = &e.this {
21472            self.write_space();
21473            self.generate_expression(this)?;
21474        }
21475        Ok(())
21476    }
21477
21478    fn generate_computed_column_constraint(&mut self, e: &ComputedColumnConstraint) -> Result<()> {
21479        // Python: return f"AS {this}{persisted}"
21480        self.write_keyword("AS");
21481        self.write_space();
21482        self.generate_expression(&e.this)?;
21483        if e.not_null.is_some() {
21484            self.write_space();
21485            self.write_keyword("PERSISTED NOT NULL");
21486        } else if e.persisted.is_some() {
21487            self.write_space();
21488            self.write_keyword("PERSISTED");
21489        }
21490        Ok(())
21491    }
21492
21493    /// Generate a ComputedColumn constraint inline within a column definition.
21494    /// Handles MySQL/PostgreSQL: GENERATED ALWAYS AS (expr) STORED|VIRTUAL
21495    /// Handles TSQL: AS (expr) [PERSISTED] [NOT NULL]
21496    fn generate_computed_column_inline(&mut self, cc: &ComputedColumn) -> Result<()> {
21497        let computed_expr = if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21498            match &*cc.expression {
21499                Expression::Year(y)
21500                    if !matches!(&y.this, Expression::Cast(c) if matches!(c.to, DataType::Date)) =>
21501                {
21502                    let wrapped = Expression::Cast(Box::new(Cast {
21503                        this: y.this.clone(),
21504                        to: DataType::Date,
21505                        trailing_comments: Vec::new(),
21506                        double_colon_syntax: false,
21507                        format: None,
21508                        default: None,
21509                    }));
21510                    Expression::Year(Box::new(UnaryFunc::new(wrapped)))
21511                }
21512                Expression::Function(f)
21513                    if f.name.eq_ignore_ascii_case("YEAR")
21514                        && f.args.len() == 1
21515                        && !matches!(&f.args[0], Expression::Cast(c) if matches!(c.to, DataType::Date)) =>
21516                {
21517                    let wrapped = Expression::Cast(Box::new(Cast {
21518                        this: f.args[0].clone(),
21519                        to: DataType::Date,
21520                        trailing_comments: Vec::new(),
21521                        double_colon_syntax: false,
21522                        format: None,
21523                        default: None,
21524                    }));
21525                    Expression::Function(Box::new(Function::new("YEAR".to_string(), vec![wrapped])))
21526                }
21527                _ => *cc.expression.clone(),
21528            }
21529        } else {
21530            *cc.expression.clone()
21531        };
21532
21533        match cc.persistence_kind.as_deref() {
21534            Some("STORED") | Some("VIRTUAL") => {
21535                // MySQL/PostgreSQL: GENERATED ALWAYS AS (expr) STORED|VIRTUAL
21536                self.write_keyword("GENERATED ALWAYS AS");
21537                self.write(" (");
21538                self.generate_expression(&computed_expr)?;
21539                self.write(")");
21540                self.write_space();
21541                if cc.persisted {
21542                    self.write_keyword("STORED");
21543                } else {
21544                    self.write_keyword("VIRTUAL");
21545                }
21546            }
21547            Some("PERSISTED") => {
21548                // TSQL/SingleStore: AS (expr) PERSISTED [TYPE] [NOT NULL]
21549                self.write_keyword("AS");
21550                self.write(" (");
21551                self.generate_expression(&computed_expr)?;
21552                self.write(")");
21553                self.write_space();
21554                self.write_keyword("PERSISTED");
21555                // Output data type if present (SingleStore: PERSISTED TYPE NOT NULL)
21556                if let Some(ref dt) = cc.data_type {
21557                    self.write_space();
21558                    self.generate_data_type(dt)?;
21559                }
21560                if cc.not_null {
21561                    self.write_space();
21562                    self.write_keyword("NOT NULL");
21563                }
21564            }
21565            _ => {
21566                // Spark/Databricks/Hive: GENERATED ALWAYS AS (expr)
21567                // TSQL computed column without PERSISTED: AS (expr)
21568                if matches!(
21569                    self.config.dialect,
21570                    Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive)
21571                ) {
21572                    self.write_keyword("GENERATED ALWAYS AS");
21573                    self.write(" (");
21574                    self.generate_expression(&computed_expr)?;
21575                    self.write(")");
21576                } else if matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
21577                    self.write_keyword("AS");
21578                    let omit_parens = matches!(computed_expr, Expression::Year(_))
21579                        || matches!(&computed_expr, Expression::Function(f) if f.name.eq_ignore_ascii_case("YEAR"));
21580                    if omit_parens {
21581                        self.write_space();
21582                        self.generate_expression(&computed_expr)?;
21583                    } else {
21584                        self.write(" (");
21585                        self.generate_expression(&computed_expr)?;
21586                        self.write(")");
21587                    }
21588                } else {
21589                    self.write_keyword("AS");
21590                    self.write(" (");
21591                    self.generate_expression(&computed_expr)?;
21592                    self.write(")");
21593                }
21594            }
21595        }
21596        Ok(())
21597    }
21598
21599    /// Generate a GeneratedAsRow constraint inline within a column definition.
21600    /// TSQL temporal: GENERATED ALWAYS AS ROW START|END [HIDDEN]
21601    fn generate_generated_as_row_inline(&mut self, gar: &GeneratedAsRow) -> Result<()> {
21602        self.write_keyword("GENERATED ALWAYS AS ROW ");
21603        if gar.start {
21604            self.write_keyword("START");
21605        } else {
21606            self.write_keyword("END");
21607        }
21608        if gar.hidden {
21609            self.write_space();
21610            self.write_keyword("HIDDEN");
21611        }
21612        Ok(())
21613    }
21614
21615    /// Generate just the SYSTEM_VERSIONING=ON(...) content without WITH() wrapper.
21616    fn generate_system_versioning_content(&mut self, e: &WithSystemVersioningProperty) -> Result<()> {
21617        let mut parts = Vec::new();
21618
21619        if let Some(this) = &e.this {
21620            let mut s = String::from("HISTORY_TABLE=");
21621            let mut gen = Generator::new();
21622            gen.config = self.config.clone();
21623            gen.generate_expression(this)?;
21624            s.push_str(&gen.output);
21625            parts.push(s);
21626        }
21627
21628        if let Some(data_consistency) = &e.data_consistency {
21629            let mut s = String::from("DATA_CONSISTENCY_CHECK=");
21630            let mut gen = Generator::new();
21631            gen.config = self.config.clone();
21632            gen.generate_expression(data_consistency)?;
21633            s.push_str(&gen.output);
21634            parts.push(s);
21635        }
21636
21637        if let Some(retention_period) = &e.retention_period {
21638            let mut s = String::from("HISTORY_RETENTION_PERIOD=");
21639            let mut gen = Generator::new();
21640            gen.config = self.config.clone();
21641            gen.generate_expression(retention_period)?;
21642            s.push_str(&gen.output);
21643            parts.push(s);
21644        }
21645
21646        self.write_keyword("SYSTEM_VERSIONING");
21647        self.write("=");
21648
21649        if !parts.is_empty() {
21650            self.write_keyword("ON");
21651            self.write("(");
21652            self.write(&parts.join(", "));
21653            self.write(")");
21654        } else if e.on.is_some() {
21655            self.write_keyword("ON");
21656        } else {
21657            self.write_keyword("OFF");
21658        }
21659
21660        Ok(())
21661    }
21662
21663    fn generate_conditional_insert(&mut self, e: &ConditionalInsert) -> Result<()> {
21664        // Conditional INSERT for multi-table inserts
21665        // Output: [WHEN cond THEN | ELSE] INTO table [(cols)] [VALUES (...)]
21666        if e.else_.is_some() {
21667            self.write_keyword("ELSE");
21668            self.write_space();
21669        } else if let Some(expression) = &e.expression {
21670            self.write_keyword("WHEN");
21671            self.write_space();
21672            self.generate_expression(expression)?;
21673            self.write_space();
21674            self.write_keyword("THEN");
21675            self.write_space();
21676        }
21677
21678        // Handle Insert expression specially - output "INTO table (cols) VALUES (...)"
21679        // without the "INSERT " prefix
21680        if let Expression::Insert(insert) = e.this.as_ref() {
21681            self.write_keyword("INTO");
21682            self.write_space();
21683            self.generate_table(&insert.table)?;
21684
21685            // Optional column list
21686            if !insert.columns.is_empty() {
21687                self.write(" (");
21688                for (i, col) in insert.columns.iter().enumerate() {
21689                    if i > 0 {
21690                        self.write(", ");
21691                    }
21692                    self.generate_identifier(col)?;
21693                }
21694                self.write(")");
21695            }
21696
21697            // Optional VALUES clause
21698            if !insert.values.is_empty() {
21699                self.write_space();
21700                self.write_keyword("VALUES");
21701                for (row_idx, row) in insert.values.iter().enumerate() {
21702                    if row_idx > 0 {
21703                        self.write(", ");
21704                    }
21705                    self.write(" (");
21706                    for (i, val) in row.iter().enumerate() {
21707                        if i > 0 {
21708                            self.write(", ");
21709                        }
21710                        self.generate_expression(val)?;
21711                    }
21712                    self.write(")");
21713                }
21714            }
21715        } else {
21716            // Fallback for non-Insert expressions
21717            self.generate_expression(&e.this)?;
21718        }
21719        Ok(())
21720    }
21721
21722    fn generate_constraint(&mut self, e: &Constraint) -> Result<()> {
21723        // Python: return f"CONSTRAINT {this} {expressions}"
21724        self.write_keyword("CONSTRAINT");
21725        self.write_space();
21726        self.generate_expression(&e.this)?;
21727        if !e.expressions.is_empty() {
21728            self.write_space();
21729            for (i, expr) in e.expressions.iter().enumerate() {
21730                if i > 0 {
21731                    self.write_space();
21732                }
21733                self.generate_expression(expr)?;
21734            }
21735        }
21736        Ok(())
21737    }
21738
21739    fn generate_convert_timezone(&mut self, e: &ConvertTimezone) -> Result<()> {
21740        // CONVERT_TIMEZONE([source_tz,] target_tz, timestamp)
21741        self.write_keyword("CONVERT_TIMEZONE");
21742        self.write("(");
21743        let mut first = true;
21744        if let Some(source_tz) = &e.source_tz {
21745            self.generate_expression(source_tz)?;
21746            first = false;
21747        }
21748        if let Some(target_tz) = &e.target_tz {
21749            if !first {
21750                self.write(", ");
21751            }
21752            self.generate_expression(target_tz)?;
21753            first = false;
21754        }
21755        if let Some(timestamp) = &e.timestamp {
21756            if !first {
21757                self.write(", ");
21758            }
21759            self.generate_expression(timestamp)?;
21760        }
21761        self.write(")");
21762        Ok(())
21763    }
21764
21765    fn generate_convert_to_charset(&mut self, e: &ConvertToCharset) -> Result<()> {
21766        // CONVERT(this USING dest)
21767        self.write_keyword("CONVERT");
21768        self.write("(");
21769        self.generate_expression(&e.this)?;
21770        if let Some(dest) = &e.dest {
21771            self.write_space();
21772            self.write_keyword("USING");
21773            self.write_space();
21774            self.generate_expression(dest)?;
21775        }
21776        self.write(")");
21777        Ok(())
21778    }
21779
21780    fn generate_copy(&mut self, e: &CopyStmt) -> Result<()> {
21781        self.write_keyword("COPY");
21782        if e.is_into {
21783            self.write_space();
21784            self.write_keyword("INTO");
21785        }
21786        self.write_space();
21787
21788        // Generate target table or query (or stage for COPY INTO @stage)
21789        if let Expression::Literal(Literal::String(s)) = &e.this {
21790            if s.starts_with('@') {
21791                self.write(s);
21792            } else {
21793                self.generate_expression(&e.this)?;
21794            }
21795        } else {
21796            self.generate_expression(&e.this)?;
21797        }
21798
21799        // FROM or TO based on kind
21800        if e.kind {
21801            // kind=true means FROM (loading into table)
21802            if self.config.pretty {
21803                self.write_newline();
21804            } else {
21805                self.write_space();
21806            }
21807            self.write_keyword("FROM");
21808            self.write_space();
21809        } else if !e.files.is_empty() {
21810            // kind=false means TO (exporting)
21811            if self.config.pretty {
21812                self.write_newline();
21813            } else {
21814                self.write_space();
21815            }
21816            self.write_keyword("TO");
21817            self.write_space();
21818        }
21819
21820        // Generate source/destination files
21821        for (i, file) in e.files.iter().enumerate() {
21822            if i > 0 {
21823                self.write_space();
21824            }
21825            // For stage references (strings starting with @), output without quotes
21826            if let Expression::Literal(Literal::String(s)) = file {
21827                if s.starts_with('@') {
21828                    self.write(s);
21829                } else {
21830                    self.generate_expression(file)?;
21831                }
21832            } else if let Expression::Identifier(id) = file {
21833                // Backtick-quoted file path (Databricks style: `s3://link`)
21834                if id.quoted {
21835                    self.write("`");
21836                    self.write(&id.name);
21837                    self.write("`");
21838                } else {
21839                    self.generate_expression(file)?;
21840                }
21841            } else {
21842                self.generate_expression(file)?;
21843            }
21844        }
21845
21846        // Generate credentials if present (Snowflake style - not wrapped in WITH)
21847        if !e.with_wrapped {
21848            if let Some(ref creds) = e.credentials {
21849                if let Some(ref storage) = creds.storage {
21850                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21851                    self.write_keyword("STORAGE_INTEGRATION");
21852                    self.write(" = ");
21853                    self.write(storage);
21854                }
21855                if creds.credentials.is_empty() {
21856                    // Empty credentials: CREDENTIALS = ()
21857                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21858                    self.write_keyword("CREDENTIALS");
21859                    self.write(" = ()");
21860                } else {
21861                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21862                    self.write_keyword("CREDENTIALS");
21863                    // Check if this is Redshift-style (single value with empty key)
21864                    // vs Snowflake-style (multiple key=value pairs)
21865                    if creds.credentials.len() == 1 && creds.credentials[0].0.is_empty() {
21866                        // Redshift style: CREDENTIALS 'value'
21867                        self.write(" '");
21868                        self.write(&creds.credentials[0].1);
21869                        self.write("'");
21870                    } else {
21871                        // Snowflake style: CREDENTIALS = (KEY='value' ...)
21872                        self.write(" = (");
21873                        for (i, (k, v)) in creds.credentials.iter().enumerate() {
21874                            if i > 0 {
21875                                self.write_space();
21876                            }
21877                            self.write(k);
21878                            self.write("='");
21879                            self.write(v);
21880                            self.write("'");
21881                        }
21882                        self.write(")");
21883                    }
21884                }
21885                if let Some(ref encryption) = creds.encryption {
21886                    self.write_space();
21887                    self.write_keyword("ENCRYPTION");
21888                    self.write(" = ");
21889                    self.write(encryption);
21890                }
21891            }
21892        }
21893
21894        // Generate parameters
21895        if !e.params.is_empty() {
21896            if e.with_wrapped {
21897                // DuckDB/PostgreSQL/TSQL WITH (...) format
21898                self.write_space();
21899                self.write_keyword("WITH");
21900                self.write(" (");
21901                for (i, param) in e.params.iter().enumerate() {
21902                    if i > 0 {
21903                        self.write(", ");
21904                    }
21905                    self.generate_copy_param_with_format(param)?;
21906                }
21907                self.write(")");
21908            } else {
21909                // Snowflake/Redshift format: KEY = VALUE or KEY VALUE (space separated, no WITH wrapper)
21910                // For Redshift: IAM_ROLE value, CREDENTIALS 'value', REGION 'value', FORMAT type
21911                // For Snowflake: KEY = VALUE
21912                for param in &e.params {
21913                    if self.config.pretty { self.write_newline(); } else { self.write_space(); }
21914                    // Preserve original case of parameter name (important for Redshift COPY options)
21915                    self.write(&param.name);
21916                    if let Some(ref value) = param.value {
21917                        // Use = only if it was present in the original (param.eq)
21918                        if param.eq {
21919                            self.write(" = ");
21920                        } else {
21921                            self.write(" ");
21922                        }
21923                        if !param.values.is_empty() {
21924                            self.write("(");
21925                            for (i, v) in param.values.iter().enumerate() {
21926                                if i > 0 {
21927                                    self.write_space();
21928                                }
21929                                self.generate_copy_nested_param(v)?;
21930                            }
21931                            self.write(")");
21932                        } else {
21933                            // For COPY parameter values, output identifiers without quoting
21934                            self.generate_copy_param_value(value)?;
21935                        }
21936                    } else if !param.values.is_empty() {
21937                        // For varlen options like FORMAT_OPTIONS, COPY_OPTIONS - no = before (
21938                        if param.eq {
21939                            self.write(" = (");
21940                        } else {
21941                            self.write(" (");
21942                        }
21943                        // Determine separator for values inside parentheses:
21944                        // - Snowflake FILE_FORMAT = (TYPE=CSV FIELD_DELIMITER='|') → space-separated (has = before parens)
21945                        // - Databricks FORMAT_OPTIONS ('opt1'='true', 'opt2'='test') → comma-separated (no = before parens)
21946                        // - Simple value lists like FILES = ('file1', 'file2') → comma-separated
21947                        let is_key_value_pairs = param.values.first().map_or(false, |v| matches!(v, Expression::Eq(_)));
21948                        let sep = if is_key_value_pairs && param.eq { " " } else { ", " };
21949                        for (i, v) in param.values.iter().enumerate() {
21950                            if i > 0 {
21951                                self.write(sep);
21952                            }
21953                            self.generate_copy_nested_param(v)?;
21954                        }
21955                        self.write(")");
21956                    }
21957                }
21958            }
21959        }
21960
21961        Ok(())
21962    }
21963
21964    /// Generate a COPY parameter in WITH (...) format
21965    /// Handles both KEY = VALUE (TSQL) and KEY VALUE (DuckDB/PostgreSQL) formats
21966    fn generate_copy_param_with_format(&mut self, param: &CopyParameter) -> Result<()> {
21967        self.write_keyword(&param.name);
21968        if !param.values.is_empty() {
21969            // Nested values: CREDENTIAL = (IDENTITY='...', SECRET='...')
21970            self.write(" = (");
21971            for (i, v) in param.values.iter().enumerate() {
21972                if i > 0 {
21973                    self.write(", ");
21974                }
21975                self.generate_copy_nested_param(v)?;
21976            }
21977            self.write(")");
21978        } else if let Some(ref value) = param.value {
21979            if param.eq {
21980                self.write(" = ");
21981            } else {
21982                self.write(" ");
21983            }
21984            self.generate_expression(value)?;
21985        }
21986        Ok(())
21987    }
21988
21989    /// Generate nested parameter for COPY statements (KEY=VALUE without spaces)
21990    fn generate_copy_nested_param(&mut self, expr: &Expression) -> Result<()> {
21991        match expr {
21992            Expression::Eq(eq) => {
21993                // Generate key
21994                match &eq.left {
21995                    Expression::Column(c) => self.write(&c.name.name),
21996                    _ => self.generate_expression(&eq.left)?,
21997                }
21998                self.write("=");
21999                // Generate value
22000                match &eq.right {
22001                    Expression::Literal(Literal::String(s)) => {
22002                        self.write("'");
22003                        self.write(s);
22004                        self.write("'");
22005                    }
22006                    Expression::Tuple(t) => {
22007                        // For lists like NULL_IF=('', 'str1')
22008                        self.write("(");
22009                        if self.config.pretty {
22010                            self.write_newline();
22011                            self.indent_level += 1;
22012                            for (i, item) in t.expressions.iter().enumerate() {
22013                                if i > 0 {
22014                                    self.write(", ");
22015                                }
22016                                self.write_indent();
22017                                self.generate_expression(item)?;
22018                            }
22019                            self.write_newline();
22020                            self.indent_level -= 1;
22021                        } else {
22022                            for (i, item) in t.expressions.iter().enumerate() {
22023                                if i > 0 {
22024                                    self.write(", ");
22025                                }
22026                                self.generate_expression(item)?;
22027                            }
22028                        }
22029                        self.write(")");
22030                    }
22031                    _ => self.generate_expression(&eq.right)?,
22032                }
22033                Ok(())
22034            }
22035            Expression::Column(c) => {
22036                // Standalone keyword like COMPRESSION
22037                self.write(&c.name.name);
22038                Ok(())
22039            }
22040            _ => self.generate_expression(expr),
22041        }
22042    }
22043
22044    /// Generate a COPY parameter value, outputting identifiers/columns without quoting
22045    /// This is needed for Redshift-style COPY params like: IAM_ROLE default, FORMAT orc
22046    fn generate_copy_param_value(&mut self, expr: &Expression) -> Result<()> {
22047        match expr {
22048            Expression::Column(c) => {
22049                // Output identifier, preserving quotes if originally quoted
22050                if c.name.quoted {
22051                    self.write("\"");
22052                    self.write(&c.name.name);
22053                    self.write("\"");
22054                } else {
22055                    self.write(&c.name.name);
22056                }
22057                Ok(())
22058            }
22059            Expression::Identifier(id) => {
22060                // Output identifier, preserving quotes if originally quoted
22061                if id.quoted {
22062                    self.write("\"");
22063                    self.write(&id.name);
22064                    self.write("\"");
22065                } else {
22066                    self.write(&id.name);
22067                }
22068                Ok(())
22069            }
22070            Expression::Literal(Literal::String(s)) => {
22071                // Output string with quotes
22072                self.write("'");
22073                self.write(s);
22074                self.write("'");
22075                Ok(())
22076            }
22077            _ => self.generate_expression(expr),
22078        }
22079    }
22080
22081    fn generate_copy_parameter(&mut self, e: &CopyParameter) -> Result<()> {
22082        self.write_keyword(&e.name);
22083        if let Some(ref value) = e.value {
22084            if e.eq {
22085                self.write(" = ");
22086            } else {
22087                self.write(" ");
22088            }
22089            self.generate_expression(value)?;
22090        }
22091        if !e.values.is_empty() {
22092            if e.eq {
22093                self.write(" = ");
22094            } else {
22095                self.write(" ");
22096            }
22097            self.write("(");
22098            for (i, v) in e.values.iter().enumerate() {
22099                if i > 0 {
22100                    self.write(", ");
22101                }
22102                self.generate_expression(v)?;
22103            }
22104            self.write(")");
22105        }
22106        Ok(())
22107    }
22108
22109    fn generate_corr(&mut self, e: &Corr) -> Result<()> {
22110        // CORR(this, expression)
22111        self.write_keyword("CORR");
22112        self.write("(");
22113        self.generate_expression(&e.this)?;
22114        self.write(", ");
22115        self.generate_expression(&e.expression)?;
22116        self.write(")");
22117        Ok(())
22118    }
22119
22120    fn generate_cosine_distance(&mut self, e: &CosineDistance) -> Result<()> {
22121        // COSINE_DISTANCE(this, expression)
22122        self.write_keyword("COSINE_DISTANCE");
22123        self.write("(");
22124        self.generate_expression(&e.this)?;
22125        self.write(", ");
22126        self.generate_expression(&e.expression)?;
22127        self.write(")");
22128        Ok(())
22129    }
22130
22131    fn generate_covar_pop(&mut self, e: &CovarPop) -> Result<()> {
22132        // COVAR_POP(this, expression)
22133        self.write_keyword("COVAR_POP");
22134        self.write("(");
22135        self.generate_expression(&e.this)?;
22136        self.write(", ");
22137        self.generate_expression(&e.expression)?;
22138        self.write(")");
22139        Ok(())
22140    }
22141
22142    fn generate_covar_samp(&mut self, e: &CovarSamp) -> Result<()> {
22143        // COVAR_SAMP(this, expression)
22144        self.write_keyword("COVAR_SAMP");
22145        self.write("(");
22146        self.generate_expression(&e.this)?;
22147        self.write(", ");
22148        self.generate_expression(&e.expression)?;
22149        self.write(")");
22150        Ok(())
22151    }
22152
22153    fn generate_credentials(&mut self, e: &Credentials) -> Result<()> {
22154        // CREDENTIALS (key1='value1', key2='value2')
22155        self.write_keyword("CREDENTIALS");
22156        self.write(" (");
22157        for (i, (key, value)) in e.credentials.iter().enumerate() {
22158            if i > 0 {
22159                self.write(", ");
22160            }
22161            self.write(key);
22162            self.write("='");
22163            self.write(value);
22164            self.write("'");
22165        }
22166        self.write(")");
22167        Ok(())
22168    }
22169
22170    fn generate_credentials_property(&mut self, e: &CredentialsProperty) -> Result<()> {
22171        // CREDENTIALS=(expressions)
22172        self.write_keyword("CREDENTIALS");
22173        self.write("=(");
22174        for (i, expr) in e.expressions.iter().enumerate() {
22175            if i > 0 {
22176                self.write(", ");
22177            }
22178            self.generate_expression(expr)?;
22179        }
22180        self.write(")");
22181        Ok(())
22182    }
22183
22184    fn generate_cte(&mut self, e: &Cte) -> Result<()> {
22185        use crate::dialects::DialectType;
22186
22187        // Python: return f"{alias_sql}{key_expressions} AS {materialized or ''}{self.wrap(expression)}"
22188        // Output: alias [(col1, col2, ...)] AS [MATERIALIZED|NOT MATERIALIZED] (subquery)
22189        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) && !e.alias_first {
22190            self.generate_expression(&e.this)?;
22191            self.write_space();
22192            self.write_keyword("AS");
22193            self.write_space();
22194            self.generate_identifier(&e.alias)?;
22195            return Ok(());
22196        }
22197        self.write(&e.alias.name);
22198
22199        // BigQuery doesn't support column aliases in CTE definitions
22200        let skip_cte_columns = matches!(self.config.dialect, Some(DialectType::BigQuery));
22201
22202        if !e.columns.is_empty() && !skip_cte_columns {
22203            self.write("(");
22204            for (i, col) in e.columns.iter().enumerate() {
22205                if i > 0 {
22206                    self.write(", ");
22207                }
22208                self.write(&col.name);
22209            }
22210            self.write(")");
22211        }
22212        // USING KEY (columns) for DuckDB recursive CTEs
22213        if !e.key_expressions.is_empty() {
22214            self.write_space();
22215            self.write_keyword("USING KEY");
22216            self.write(" (");
22217            for (i, key) in e.key_expressions.iter().enumerate() {
22218                if i > 0 {
22219                    self.write(", ");
22220                }
22221                self.write(&key.name);
22222            }
22223            self.write(")");
22224        }
22225        self.write_space();
22226        self.write_keyword("AS");
22227        self.write_space();
22228        if let Some(materialized) = e.materialized {
22229            if materialized {
22230                self.write_keyword("MATERIALIZED");
22231            } else {
22232                self.write_keyword("NOT MATERIALIZED");
22233            }
22234            self.write_space();
22235        }
22236        self.write("(");
22237        self.generate_expression(&e.this)?;
22238        self.write(")");
22239        Ok(())
22240    }
22241
22242    fn generate_cube(&mut self, e: &Cube) -> Result<()> {
22243        // Python: return f"CUBE {self.wrap(expressions)}" if expressions else "WITH CUBE"
22244        if e.expressions.is_empty() {
22245            self.write_keyword("WITH CUBE");
22246        } else {
22247            self.write_keyword("CUBE");
22248            self.write("(");
22249            for (i, expr) in e.expressions.iter().enumerate() {
22250                if i > 0 {
22251                    self.write(", ");
22252                }
22253                self.generate_expression(expr)?;
22254            }
22255            self.write(")");
22256        }
22257        Ok(())
22258    }
22259
22260    fn generate_current_datetime(&mut self, e: &CurrentDatetime) -> Result<()> {
22261        // CURRENT_DATETIME or CURRENT_DATETIME(timezone)
22262        self.write_keyword("CURRENT_DATETIME");
22263        if let Some(this) = &e.this {
22264            self.write("(");
22265            self.generate_expression(this)?;
22266            self.write(")");
22267        }
22268        Ok(())
22269    }
22270
22271    fn generate_current_schema(&mut self, _e: &CurrentSchema) -> Result<()> {
22272        // CURRENT_SCHEMA - no arguments
22273        self.write_keyword("CURRENT_SCHEMA");
22274        Ok(())
22275    }
22276
22277    fn generate_current_schemas(&mut self, e: &CurrentSchemas) -> Result<()> {
22278        // CURRENT_SCHEMAS(include_implicit)
22279        self.write_keyword("CURRENT_SCHEMAS");
22280        self.write("(");
22281        if let Some(this) = &e.this {
22282            self.generate_expression(this)?;
22283        }
22284        self.write(")");
22285        Ok(())
22286    }
22287
22288    fn generate_current_user(&mut self, e: &CurrentUser) -> Result<()> {
22289        // CURRENT_USER or CURRENT_USER()
22290        self.write_keyword("CURRENT_USER");
22291        // Some dialects always need parens: Snowflake, Spark, Hive, DuckDB, BigQuery, MySQL, Databricks
22292        let needs_parens = e.this.is_some() || matches!(
22293            self.config.dialect,
22294            Some(DialectType::Snowflake) | Some(DialectType::Spark)
22295            | Some(DialectType::Hive) | Some(DialectType::DuckDB) | Some(DialectType::BigQuery)
22296            | Some(DialectType::MySQL) | Some(DialectType::Databricks)
22297        );
22298        if needs_parens {
22299            self.write("()");
22300        }
22301        Ok(())
22302    }
22303
22304    fn generate_d_pipe(&mut self, e: &DPipe) -> Result<()> {
22305        // In Solr, || is OR, not string concatenation (DPIPE_IS_STRING_CONCAT = False)
22306        if self.config.dialect == Some(DialectType::Solr) {
22307            self.generate_expression(&e.this)?;
22308            self.write(" ");
22309            self.write_keyword("OR");
22310            self.write(" ");
22311            self.generate_expression(&e.expression)?;
22312        } else {
22313            // String concatenation: this || expression
22314            self.generate_expression(&e.this)?;
22315            self.write(" || ");
22316            self.generate_expression(&e.expression)?;
22317        }
22318        Ok(())
22319    }
22320
22321    fn generate_data_blocksize_property(&mut self, e: &DataBlocksizeProperty) -> Result<()> {
22322        // DATABLOCKSIZE=... (Teradata)
22323        self.write_keyword("DATABLOCKSIZE");
22324        self.write("=");
22325        if let Some(size) = e.size {
22326            self.write(&size.to_string());
22327            if let Some(units) = &e.units {
22328                self.write_space();
22329                self.generate_expression(units)?;
22330            }
22331        } else if e.minimum.is_some() {
22332            self.write_keyword("MINIMUM");
22333        } else if e.maximum.is_some() {
22334            self.write_keyword("MAXIMUM");
22335        } else if e.default.is_some() {
22336            self.write_keyword("DEFAULT");
22337        }
22338        Ok(())
22339    }
22340
22341    fn generate_data_deletion_property(&mut self, e: &DataDeletionProperty) -> Result<()> {
22342        // DATA_DELETION=ON or DATA_DELETION=OFF or DATA_DELETION=ON(FILTER_COLUMN=col, RETENTION_PERIOD=...)
22343        self.write_keyword("DATA_DELETION");
22344        self.write("=");
22345
22346        let is_on = matches!(&*e.on, Expression::Boolean(BooleanLiteral { value: true }));
22347        let has_options = e.filter_column.is_some() || e.retention_period.is_some();
22348
22349        if is_on {
22350            self.write_keyword("ON");
22351            if has_options {
22352                self.write("(");
22353                let mut first = true;
22354                if let Some(filter_column) = &e.filter_column {
22355                    self.write_keyword("FILTER_COLUMN");
22356                    self.write("=");
22357                    self.generate_expression(filter_column)?;
22358                    first = false;
22359                }
22360                if let Some(retention_period) = &e.retention_period {
22361                    if !first {
22362                        self.write(", ");
22363                    }
22364                    self.write_keyword("RETENTION_PERIOD");
22365                    self.write("=");
22366                    self.generate_expression(retention_period)?;
22367                }
22368                self.write(")");
22369            }
22370        } else {
22371            self.write_keyword("OFF");
22372        }
22373        Ok(())
22374    }
22375
22376    /// Generate a Date function expression
22377    /// For Exasol: {d'value'} -> TO_DATE('value')
22378    /// For other dialects: DATE('value')
22379    fn generate_date_func(&mut self, e: &UnaryFunc) -> Result<()> {
22380        use crate::dialects::DialectType;
22381        use crate::expressions::Literal;
22382
22383        match self.config.dialect {
22384            // Exasol uses TO_DATE for Date expressions
22385            Some(DialectType::Exasol) => {
22386                self.write_keyword("TO_DATE");
22387                self.write("(");
22388                // Extract the string value from the expression if it's a string literal
22389                match &e.this {
22390                    Expression::Literal(Literal::String(s)) => {
22391                        self.write("'");
22392                        self.write(s);
22393                        self.write("'");
22394                    }
22395                    _ => {
22396                        self.generate_expression(&e.this)?;
22397                    }
22398                }
22399                self.write(")");
22400            }
22401            // Standard: DATE(value)
22402            _ => {
22403                self.write_keyword("DATE");
22404                self.write("(");
22405                self.generate_expression(&e.this)?;
22406                self.write(")");
22407            }
22408        }
22409        Ok(())
22410    }
22411
22412    fn generate_date_bin(&mut self, e: &DateBin) -> Result<()> {
22413        // DATE_BIN(interval, timestamp[, origin])
22414        self.write_keyword("DATE_BIN");
22415        self.write("(");
22416        self.generate_expression(&e.this)?;
22417        self.write(", ");
22418        self.generate_expression(&e.expression)?;
22419        if let Some(origin) = &e.origin {
22420            self.write(", ");
22421            self.generate_expression(origin)?;
22422        }
22423        self.write(")");
22424        Ok(())
22425    }
22426
22427    fn generate_date_format_column_constraint(&mut self, e: &DateFormatColumnConstraint) -> Result<()> {
22428        // FORMAT 'format_string' (Teradata)
22429        self.write_keyword("FORMAT");
22430        self.write_space();
22431        self.generate_expression(&e.this)?;
22432        Ok(())
22433    }
22434
22435    fn generate_date_from_parts(&mut self, e: &DateFromParts) -> Result<()> {
22436        // DATE_FROM_PARTS(year, month, day) or DATEFROMPARTS(year, month, day)
22437        self.write_keyword("DATE_FROM_PARTS");
22438        self.write("(");
22439        let mut first = true;
22440        if let Some(year) = &e.year {
22441            self.generate_expression(year)?;
22442            first = false;
22443        }
22444        if let Some(month) = &e.month {
22445            if !first {
22446                self.write(", ");
22447            }
22448            self.generate_expression(month)?;
22449            first = false;
22450        }
22451        if let Some(day) = &e.day {
22452            if !first {
22453                self.write(", ");
22454            }
22455            self.generate_expression(day)?;
22456        }
22457        self.write(")");
22458        Ok(())
22459    }
22460
22461    fn generate_datetime(&mut self, e: &Datetime) -> Result<()> {
22462        // DATETIME(this) or DATETIME(this, expression)
22463        self.write_keyword("DATETIME");
22464        self.write("(");
22465        self.generate_expression(&e.this)?;
22466        if let Some(expr) = &e.expression {
22467            self.write(", ");
22468            self.generate_expression(expr)?;
22469        }
22470        self.write(")");
22471        Ok(())
22472    }
22473
22474    fn generate_datetime_add(&mut self, e: &DatetimeAdd) -> Result<()> {
22475        // DATETIME_ADD(this, expression, unit)
22476        self.write_keyword("DATETIME_ADD");
22477        self.write("(");
22478        self.generate_expression(&e.this)?;
22479        self.write(", ");
22480        self.generate_expression(&e.expression)?;
22481        if let Some(unit) = &e.unit {
22482            self.write(", ");
22483            self.write_keyword(unit);
22484        }
22485        self.write(")");
22486        Ok(())
22487    }
22488
22489    fn generate_datetime_diff(&mut self, e: &DatetimeDiff) -> Result<()> {
22490        // DATETIME_DIFF(this, expression, unit)
22491        self.write_keyword("DATETIME_DIFF");
22492        self.write("(");
22493        self.generate_expression(&e.this)?;
22494        self.write(", ");
22495        self.generate_expression(&e.expression)?;
22496        if let Some(unit) = &e.unit {
22497            self.write(", ");
22498            self.write_keyword(unit);
22499        }
22500        self.write(")");
22501        Ok(())
22502    }
22503
22504    fn generate_datetime_sub(&mut self, e: &DatetimeSub) -> Result<()> {
22505        // DATETIME_SUB(this, expression, unit)
22506        self.write_keyword("DATETIME_SUB");
22507        self.write("(");
22508        self.generate_expression(&e.this)?;
22509        self.write(", ");
22510        self.generate_expression(&e.expression)?;
22511        if let Some(unit) = &e.unit {
22512            self.write(", ");
22513            self.write_keyword(unit);
22514        }
22515        self.write(")");
22516        Ok(())
22517    }
22518
22519    fn generate_datetime_trunc(&mut self, e: &DatetimeTrunc) -> Result<()> {
22520        // DATETIME_TRUNC(this, unit, zone)
22521        self.write_keyword("DATETIME_TRUNC");
22522        self.write("(");
22523        self.generate_expression(&e.this)?;
22524        self.write(", ");
22525        self.write_keyword(&e.unit);
22526        if let Some(zone) = &e.zone {
22527            self.write(", ");
22528            self.generate_expression(zone)?;
22529        }
22530        self.write(")");
22531        Ok(())
22532    }
22533
22534    fn generate_dayname(&mut self, e: &Dayname) -> Result<()> {
22535        // DAYNAME(this)
22536        self.write_keyword("DAYNAME");
22537        self.write("(");
22538        self.generate_expression(&e.this)?;
22539        self.write(")");
22540        Ok(())
22541    }
22542
22543    fn generate_declare(&mut self, e: &Declare) -> Result<()> {
22544        // DECLARE var1 AS type1, var2 AS type2, ...
22545        self.write_keyword("DECLARE");
22546        self.write_space();
22547        for (i, expr) in e.expressions.iter().enumerate() {
22548            if i > 0 {
22549                self.write(", ");
22550            }
22551            self.generate_expression(expr)?;
22552        }
22553        Ok(())
22554    }
22555
22556    fn generate_declare_item(&mut self, e: &DeclareItem) -> Result<()> {
22557        use crate::dialects::DialectType;
22558
22559        // variable TYPE [DEFAULT default]
22560        self.generate_expression(&e.this)?;
22561        // BigQuery multi-variable: DECLARE X, Y, Z INT64
22562        for name in &e.additional_names {
22563            self.write(", ");
22564            self.generate_expression(name)?;
22565        }
22566        if let Some(kind) = &e.kind {
22567            self.write_space();
22568            // BigQuery uses: DECLARE x INT64 DEFAULT value (no AS)
22569            // TSQL: Always includes AS (normalization)
22570            // Others: Include AS if present in original
22571            match self.config.dialect {
22572                Some(DialectType::BigQuery) => {
22573                    self.write(kind);
22574                }
22575                Some(DialectType::TSQL) => {
22576                    // TSQL: Check for complex TABLE constraints that should be passed through unchanged
22577                    // Python sqlglot falls back to Command for TABLE declarations with CLUSTERED,
22578                    // NONCLUSTERED, or INDEX constraints
22579                    let is_complex_table = kind.starts_with("TABLE") &&
22580                        (kind.contains("CLUSTERED") || kind.contains("INDEX"));
22581
22582                    if is_complex_table {
22583                        // Complex TABLE declarations: preserve as-is (no AS, no INT normalization)
22584                        self.write(kind);
22585                    } else {
22586                        // Simple declarations: add AS (except for CURSOR) and normalize INT
22587                        if !kind.starts_with("CURSOR") {
22588                            self.write_keyword("AS");
22589                            self.write_space();
22590                        }
22591                        // Normalize INT to INTEGER for TSQL DECLARE statements
22592                        if kind == "INT" {
22593                            self.write("INTEGER");
22594                        } else if kind.starts_with("TABLE") {
22595                            // Normalize INT to INTEGER inside TABLE column definitions
22596                            let normalized = kind
22597                                .replace(" INT ", " INTEGER ")
22598                                .replace(" INT,", " INTEGER,")
22599                                .replace(" INT)", " INTEGER)")
22600                                .replace("(INT ", "(INTEGER ");
22601                            self.write(&normalized);
22602                        } else {
22603                            self.write(kind);
22604                        }
22605                    }
22606                }
22607                _ => {
22608                    if e.has_as {
22609                        self.write_keyword("AS");
22610                        self.write_space();
22611                    }
22612                    self.write(kind);
22613                }
22614            }
22615        }
22616        if let Some(default) = &e.default {
22617            // BigQuery uses DEFAULT, others use =
22618            match self.config.dialect {
22619                Some(DialectType::BigQuery) => {
22620                    self.write_space();
22621                    self.write_keyword("DEFAULT");
22622                    self.write_space();
22623                }
22624                _ => {
22625                    self.write(" = ");
22626                }
22627            }
22628            self.generate_expression(default)?;
22629        }
22630        Ok(())
22631    }
22632
22633    fn generate_decode_case(&mut self, e: &DecodeCase) -> Result<()> {
22634        // DECODE(expr, search1, result1, search2, result2, ..., default)
22635        self.write_keyword("DECODE");
22636        self.write("(");
22637        for (i, expr) in e.expressions.iter().enumerate() {
22638            if i > 0 {
22639                self.write(", ");
22640            }
22641            self.generate_expression(expr)?;
22642        }
22643        self.write(")");
22644        Ok(())
22645    }
22646
22647    fn generate_decompress_binary(&mut self, e: &DecompressBinary) -> Result<()> {
22648        // DECOMPRESS(expr, 'method')
22649        self.write_keyword("DECOMPRESS");
22650        self.write("(");
22651        self.generate_expression(&e.this)?;
22652        self.write(", '");
22653        self.write(&e.method);
22654        self.write("')");
22655        Ok(())
22656    }
22657
22658    fn generate_decompress_string(&mut self, e: &DecompressString) -> Result<()> {
22659        // DECOMPRESS(expr, 'method')
22660        self.write_keyword("DECOMPRESS");
22661        self.write("(");
22662        self.generate_expression(&e.this)?;
22663        self.write(", '");
22664        self.write(&e.method);
22665        self.write("')");
22666        Ok(())
22667    }
22668
22669    fn generate_decrypt(&mut self, e: &Decrypt) -> Result<()> {
22670        // DECRYPT(value, passphrase [, aad [, algorithm]])
22671        self.write_keyword("DECRYPT");
22672        self.write("(");
22673        self.generate_expression(&e.this)?;
22674        if let Some(passphrase) = &e.passphrase {
22675            self.write(", ");
22676            self.generate_expression(passphrase)?;
22677        }
22678        if let Some(aad) = &e.aad {
22679            self.write(", ");
22680            self.generate_expression(aad)?;
22681        }
22682        if let Some(method) = &e.encryption_method {
22683            self.write(", ");
22684            self.generate_expression(method)?;
22685        }
22686        self.write(")");
22687        Ok(())
22688    }
22689
22690    fn generate_decrypt_raw(&mut self, e: &DecryptRaw) -> Result<()> {
22691        // DECRYPT_RAW(value, key [, iv [, aad [, algorithm]]])
22692        self.write_keyword("DECRYPT_RAW");
22693        self.write("(");
22694        self.generate_expression(&e.this)?;
22695        if let Some(key) = &e.key {
22696            self.write(", ");
22697            self.generate_expression(key)?;
22698        }
22699        if let Some(iv) = &e.iv {
22700            self.write(", ");
22701            self.generate_expression(iv)?;
22702        }
22703        if let Some(aad) = &e.aad {
22704            self.write(", ");
22705            self.generate_expression(aad)?;
22706        }
22707        if let Some(method) = &e.encryption_method {
22708            self.write(", ");
22709            self.generate_expression(method)?;
22710        }
22711        self.write(")");
22712        Ok(())
22713    }
22714
22715    fn generate_definer_property(&mut self, e: &DefinerProperty) -> Result<()> {
22716        // DEFINER = user
22717        self.write_keyword("DEFINER");
22718        self.write(" = ");
22719        self.generate_expression(&e.this)?;
22720        Ok(())
22721    }
22722
22723    fn generate_detach(&mut self, e: &Detach) -> Result<()> {
22724        // Python: DETACH[DATABASE IF EXISTS] this
22725        self.write_keyword("DETACH");
22726        if e.exists {
22727            self.write_keyword(" DATABASE IF EXISTS");
22728        }
22729        self.write_space();
22730        self.generate_expression(&e.this)?;
22731        Ok(())
22732    }
22733
22734    fn generate_dict_property(&mut self, e: &DictProperty) -> Result<()> {
22735        let property_name = match e.this.as_ref() {
22736            Expression::Identifier(id) => id.name.as_str(),
22737            Expression::Var(v) => v.this.as_str(),
22738            _ => "DICTIONARY",
22739        };
22740        self.write_keyword(property_name);
22741        self.write("(");
22742        self.write(&e.kind);
22743        if let Some(settings) = &e.settings {
22744            self.write("(");
22745            if let Expression::Tuple(t) = settings.as_ref() {
22746                if self.config.pretty && !t.expressions.is_empty() {
22747                    self.write_newline();
22748                    self.indent_level += 1;
22749                    for (i, pair) in t.expressions.iter().enumerate() {
22750                        if i > 0 {
22751                            self.write(",");
22752                            self.write_newline();
22753                        }
22754                        self.write_indent();
22755                        if let Expression::Tuple(pair_tuple) = pair {
22756                            if let Some(k) = pair_tuple.expressions.first() {
22757                                self.generate_expression(k)?;
22758                            }
22759                            if let Some(v) = pair_tuple.expressions.get(1) {
22760                                self.write(" ");
22761                                self.generate_expression(v)?;
22762                            }
22763                        } else {
22764                            self.generate_expression(pair)?;
22765                        }
22766                    }
22767                    self.indent_level -= 1;
22768                    self.write_newline();
22769                    self.write_indent();
22770                } else {
22771                    for (i, pair) in t.expressions.iter().enumerate() {
22772                        if i > 0 {
22773                            self.write(", ");
22774                        }
22775                        if let Expression::Tuple(pair_tuple) = pair {
22776                            if let Some(k) = pair_tuple.expressions.first() {
22777                                self.generate_expression(k)?;
22778                            }
22779                            if let Some(v) = pair_tuple.expressions.get(1) {
22780                                self.write(" ");
22781                                self.generate_expression(v)?;
22782                            }
22783                        } else {
22784                            self.generate_expression(pair)?;
22785                        }
22786                    }
22787                }
22788            } else {
22789                self.generate_expression(settings)?;
22790            }
22791            self.write(")");
22792        } else if property_name.eq_ignore_ascii_case("LAYOUT") {
22793            self.write("()");
22794        }
22795        self.write(")");
22796        Ok(())
22797    }
22798
22799    fn generate_dict_range(&mut self, e: &DictRange) -> Result<()> {
22800        let property_name = match e.this.as_ref() {
22801            Expression::Identifier(id) => id.name.as_str(),
22802            Expression::Var(v) => v.this.as_str(),
22803            _ => "RANGE",
22804        };
22805        self.write_keyword(property_name);
22806        self.write("(");
22807        if let Some(min) = &e.min {
22808            self.write_keyword("MIN");
22809            self.write_space();
22810            self.generate_expression(min)?;
22811        }
22812        if let Some(max) = &e.max {
22813            self.write_space();
22814            self.write_keyword("MAX");
22815            self.write_space();
22816            self.generate_expression(max)?;
22817        }
22818        self.write(")");
22819        Ok(())
22820    }
22821
22822    fn generate_directory(&mut self, e: &Directory) -> Result<()> {
22823        // Python: {local}DIRECTORY {this}{row_format}
22824        if e.local.is_some() {
22825            self.write_keyword("LOCAL ");
22826        }
22827        self.write_keyword("DIRECTORY");
22828        self.write_space();
22829        self.generate_expression(&e.this)?;
22830        if let Some(row_format) = &e.row_format {
22831            self.write_space();
22832            self.generate_expression(row_format)?;
22833        }
22834        Ok(())
22835    }
22836
22837    fn generate_dist_key_property(&mut self, e: &DistKeyProperty) -> Result<()> {
22838        // Redshift: DISTKEY(column)
22839        self.write_keyword("DISTKEY");
22840        self.write("(");
22841        self.generate_expression(&e.this)?;
22842        self.write(")");
22843        Ok(())
22844    }
22845
22846    fn generate_dist_style_property(&mut self, e: &DistStyleProperty) -> Result<()> {
22847        // Redshift: DISTSTYLE KEY|ALL|EVEN|AUTO
22848        self.write_keyword("DISTSTYLE");
22849        self.write_space();
22850        self.generate_expression(&e.this)?;
22851        Ok(())
22852    }
22853
22854    fn generate_distribute_by(&mut self, e: &DistributeBy) -> Result<()> {
22855        // Python: "DISTRIBUTE BY" expressions
22856        self.write_keyword("DISTRIBUTE BY");
22857        self.write_space();
22858        for (i, expr) in e.expressions.iter().enumerate() {
22859            if i > 0 {
22860                self.write(", ");
22861            }
22862            self.generate_expression(expr)?;
22863        }
22864        Ok(())
22865    }
22866
22867    fn generate_distributed_by_property(&mut self, e: &DistributedByProperty) -> Result<()> {
22868        // Python: DISTRIBUTED BY kind (expressions) BUCKETS buckets order
22869        self.write_keyword("DISTRIBUTED BY");
22870        self.write_space();
22871        self.write(&e.kind);
22872        if !e.expressions.is_empty() {
22873            self.write(" (");
22874            for (i, expr) in e.expressions.iter().enumerate() {
22875                if i > 0 {
22876                    self.write(", ");
22877                }
22878                self.generate_expression(expr)?;
22879            }
22880            self.write(")");
22881        }
22882        if let Some(buckets) = &e.buckets {
22883            self.write_space();
22884            self.write_keyword("BUCKETS");
22885            self.write_space();
22886            self.generate_expression(buckets)?;
22887        }
22888        if let Some(order) = &e.order {
22889            self.write_space();
22890            self.generate_expression(order)?;
22891        }
22892        Ok(())
22893    }
22894
22895    fn generate_dot_product(&mut self, e: &DotProduct) -> Result<()> {
22896        // DOT_PRODUCT(vector1, vector2)
22897        self.write_keyword("DOT_PRODUCT");
22898        self.write("(");
22899        self.generate_expression(&e.this)?;
22900        self.write(", ");
22901        self.generate_expression(&e.expression)?;
22902        self.write(")");
22903        Ok(())
22904    }
22905
22906    fn generate_drop_partition(&mut self, e: &DropPartition) -> Result<()> {
22907        // Python: DROP{IF EXISTS }expressions
22908        self.write_keyword("DROP");
22909        if e.exists {
22910            self.write_keyword(" IF EXISTS ");
22911        } else {
22912            self.write_space();
22913        }
22914        for (i, expr) in e.expressions.iter().enumerate() {
22915            if i > 0 {
22916                self.write(", ");
22917            }
22918            self.generate_expression(expr)?;
22919        }
22920        Ok(())
22921    }
22922
22923    fn generate_duplicate_key_property(&mut self, e: &DuplicateKeyProperty) -> Result<()> {
22924        // Python: DUPLICATE KEY (expressions)
22925        self.write_keyword("DUPLICATE KEY");
22926        self.write(" (");
22927        for (i, expr) in e.expressions.iter().enumerate() {
22928            if i > 0 {
22929                self.write(", ");
22930            }
22931            self.generate_expression(expr)?;
22932        }
22933        self.write(")");
22934        Ok(())
22935    }
22936
22937    fn generate_elt(&mut self, e: &Elt) -> Result<()> {
22938        // ELT(index, str1, str2, ...)
22939        self.write_keyword("ELT");
22940        self.write("(");
22941        self.generate_expression(&e.this)?;
22942        for expr in &e.expressions {
22943            self.write(", ");
22944            self.generate_expression(expr)?;
22945        }
22946        self.write(")");
22947        Ok(())
22948    }
22949
22950    fn generate_encode(&mut self, e: &Encode) -> Result<()> {
22951        // ENCODE(string, charset)
22952        self.write_keyword("ENCODE");
22953        self.write("(");
22954        self.generate_expression(&e.this)?;
22955        if let Some(charset) = &e.charset {
22956            self.write(", ");
22957            self.generate_expression(charset)?;
22958        }
22959        self.write(")");
22960        Ok(())
22961    }
22962
22963    fn generate_encode_property(&mut self, e: &EncodeProperty) -> Result<()> {
22964        // Python: [KEY ]ENCODE this [properties]
22965        if e.key.is_some() {
22966            self.write_keyword("KEY ");
22967        }
22968        self.write_keyword("ENCODE");
22969        self.write_space();
22970        self.generate_expression(&e.this)?;
22971        if !e.properties.is_empty() {
22972            self.write(" (");
22973            for (i, prop) in e.properties.iter().enumerate() {
22974                if i > 0 {
22975                    self.write(", ");
22976                }
22977                self.generate_expression(prop)?;
22978            }
22979            self.write(")");
22980        }
22981        Ok(())
22982    }
22983
22984    fn generate_encrypt(&mut self, e: &Encrypt) -> Result<()> {
22985        // ENCRYPT(value, passphrase [, aad [, algorithm]])
22986        self.write_keyword("ENCRYPT");
22987        self.write("(");
22988        self.generate_expression(&e.this)?;
22989        if let Some(passphrase) = &e.passphrase {
22990            self.write(", ");
22991            self.generate_expression(passphrase)?;
22992        }
22993        if let Some(aad) = &e.aad {
22994            self.write(", ");
22995            self.generate_expression(aad)?;
22996        }
22997        if let Some(method) = &e.encryption_method {
22998            self.write(", ");
22999            self.generate_expression(method)?;
23000        }
23001        self.write(")");
23002        Ok(())
23003    }
23004
23005    fn generate_encrypt_raw(&mut self, e: &EncryptRaw) -> Result<()> {
23006        // ENCRYPT_RAW(value, key [, iv [, aad [, algorithm]]])
23007        self.write_keyword("ENCRYPT_RAW");
23008        self.write("(");
23009        self.generate_expression(&e.this)?;
23010        if let Some(key) = &e.key {
23011            self.write(", ");
23012            self.generate_expression(key)?;
23013        }
23014        if let Some(iv) = &e.iv {
23015            self.write(", ");
23016            self.generate_expression(iv)?;
23017        }
23018        if let Some(aad) = &e.aad {
23019            self.write(", ");
23020            self.generate_expression(aad)?;
23021        }
23022        if let Some(method) = &e.encryption_method {
23023            self.write(", ");
23024            self.generate_expression(method)?;
23025        }
23026        self.write(")");
23027        Ok(())
23028    }
23029
23030    fn generate_engine_property(&mut self, e: &EngineProperty) -> Result<()> {
23031        // MySQL: ENGINE = InnoDB
23032        self.write_keyword("ENGINE");
23033        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
23034            self.write("=");
23035        } else {
23036            self.write(" = ");
23037        }
23038        self.generate_expression(&e.this)?;
23039        Ok(())
23040    }
23041
23042    fn generate_enviroment_property(&mut self, e: &EnviromentProperty) -> Result<()> {
23043        // ENVIRONMENT (expressions)
23044        self.write_keyword("ENVIRONMENT");
23045        self.write(" (");
23046        for (i, expr) in e.expressions.iter().enumerate() {
23047            if i > 0 {
23048                self.write(", ");
23049            }
23050            self.generate_expression(expr)?;
23051        }
23052        self.write(")");
23053        Ok(())
23054    }
23055
23056    fn generate_ephemeral_column_constraint(&mut self, e: &EphemeralColumnConstraint) -> Result<()> {
23057        // MySQL: EPHEMERAL [expr]
23058        self.write_keyword("EPHEMERAL");
23059        if let Some(this) = &e.this {
23060            self.write_space();
23061            self.generate_expression(this)?;
23062        }
23063        Ok(())
23064    }
23065
23066    fn generate_equal_null(&mut self, e: &EqualNull) -> Result<()> {
23067        // Snowflake: EQUAL_NULL(a, b)
23068        self.write_keyword("EQUAL_NULL");
23069        self.write("(");
23070        self.generate_expression(&e.this)?;
23071        self.write(", ");
23072        self.generate_expression(&e.expression)?;
23073        self.write(")");
23074        Ok(())
23075    }
23076
23077    fn generate_euclidean_distance(&mut self, e: &EuclideanDistance) -> Result<()> {
23078        use crate::dialects::DialectType;
23079
23080        // PostgreSQL uses <-> operator syntax
23081        match self.config.dialect {
23082            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
23083                self.generate_expression(&e.this)?;
23084                self.write(" <-> ");
23085                self.generate_expression(&e.expression)?;
23086            }
23087            _ => {
23088                // Other dialects use EUCLIDEAN_DISTANCE function
23089                self.write_keyword("EUCLIDEAN_DISTANCE");
23090                self.write("(");
23091                self.generate_expression(&e.this)?;
23092                self.write(", ");
23093                self.generate_expression(&e.expression)?;
23094                self.write(")");
23095            }
23096        }
23097        Ok(())
23098    }
23099
23100    fn generate_execute_as_property(&mut self, e: &ExecuteAsProperty) -> Result<()> {
23101        // EXECUTE AS CALLER|OWNER|user
23102        self.write_keyword("EXECUTE AS");
23103        self.write_space();
23104        self.generate_expression(&e.this)?;
23105        Ok(())
23106    }
23107
23108    fn generate_export(&mut self, e: &Export) -> Result<()> {
23109        // BigQuery: EXPORT DATA [WITH CONNECTION connection] OPTIONS (...) AS query
23110        self.write_keyword("EXPORT DATA");
23111        if let Some(connection) = &e.connection {
23112            self.write_space();
23113            self.write_keyword("WITH CONNECTION");
23114            self.write_space();
23115            self.generate_expression(connection)?;
23116        }
23117        if !e.options.is_empty() {
23118            self.write_space();
23119            self.generate_options_clause(&e.options)?;
23120        }
23121        self.write_space();
23122        self.write_keyword("AS");
23123        self.write_space();
23124        self.generate_expression(&e.this)?;
23125        Ok(())
23126    }
23127
23128    fn generate_external_property(&mut self, e: &ExternalProperty) -> Result<()> {
23129        // EXTERNAL [this]
23130        self.write_keyword("EXTERNAL");
23131        if let Some(this) = &e.this {
23132            self.write_space();
23133            self.generate_expression(this)?;
23134        }
23135        Ok(())
23136    }
23137
23138    fn generate_fallback_property(&mut self, e: &FallbackProperty) -> Result<()> {
23139        // Python: {no}FALLBACK{protection}
23140        if e.no.is_some() {
23141            self.write_keyword("NO ");
23142        }
23143        self.write_keyword("FALLBACK");
23144        if e.protection.is_some() {
23145            self.write_keyword(" PROTECTION");
23146        }
23147        Ok(())
23148    }
23149
23150    fn generate_farm_fingerprint(&mut self, e: &FarmFingerprint) -> Result<()> {
23151        // BigQuery: FARM_FINGERPRINT(value)
23152        self.write_keyword("FARM_FINGERPRINT");
23153        self.write("(");
23154        for (i, expr) in e.expressions.iter().enumerate() {
23155            if i > 0 {
23156                self.write(", ");
23157            }
23158            self.generate_expression(expr)?;
23159        }
23160        self.write(")");
23161        Ok(())
23162    }
23163
23164    fn generate_features_at_time(&mut self, e: &FeaturesAtTime) -> Result<()> {
23165        // BigQuery ML: FEATURES_AT_TIME(feature_view, time, [num_rows], [ignore_feature_nulls])
23166        self.write_keyword("FEATURES_AT_TIME");
23167        self.write("(");
23168        self.generate_expression(&e.this)?;
23169        if let Some(time) = &e.time {
23170            self.write(", ");
23171            self.generate_expression(time)?;
23172        }
23173        if let Some(num_rows) = &e.num_rows {
23174            self.write(", ");
23175            self.generate_expression(num_rows)?;
23176        }
23177        if let Some(ignore_nulls) = &e.ignore_feature_nulls {
23178            self.write(", ");
23179            self.generate_expression(ignore_nulls)?;
23180        }
23181        self.write(")");
23182        Ok(())
23183    }
23184
23185    fn generate_fetch(&mut self, e: &Fetch) -> Result<()> {
23186        // For dialects that prefer LIMIT, convert simple FETCH to LIMIT
23187        let use_limit = !e.percent && !e.with_ties && e.count.is_some() && matches!(
23188            self.config.dialect,
23189            Some(DialectType::Spark) | Some(DialectType::Hive)
23190            | Some(DialectType::DuckDB) | Some(DialectType::SQLite) | Some(DialectType::MySQL)
23191            | Some(DialectType::BigQuery) | Some(DialectType::Databricks) | Some(DialectType::StarRocks)
23192            | Some(DialectType::Doris) | Some(DialectType::Athena) | Some(DialectType::ClickHouse)
23193        );
23194
23195        if use_limit {
23196            self.write_keyword("LIMIT");
23197            self.write_space();
23198            self.generate_expression(e.count.as_ref().unwrap())?;
23199            return Ok(());
23200        }
23201
23202        // Python: FETCH direction count limit_options
23203        self.write_keyword("FETCH");
23204        if !e.direction.is_empty() {
23205            self.write_space();
23206            self.write_keyword(&e.direction);
23207        }
23208        if let Some(count) = &e.count {
23209            self.write_space();
23210            self.generate_expression(count)?;
23211        }
23212        // Generate PERCENT, ROWS, WITH TIES/ONLY
23213        if e.percent {
23214            self.write_keyword(" PERCENT");
23215        }
23216        if e.rows {
23217            self.write_keyword(" ROWS");
23218        }
23219        if e.with_ties {
23220            self.write_keyword(" WITH TIES");
23221        } else if e.rows {
23222            self.write_keyword(" ONLY");
23223        } else {
23224            self.write_keyword(" ROWS ONLY");
23225        }
23226        Ok(())
23227    }
23228
23229    fn generate_file_format_property(&mut self, e: &FileFormatProperty) -> Result<()> {
23230        // For Hive format: STORED AS this or STORED AS INPUTFORMAT x OUTPUTFORMAT y
23231        // For Spark/Databricks without hive_format: USING this
23232        // For Snowflake/others: FILE_FORMAT = this or FILE_FORMAT = (expressions)
23233        if e.hive_format.is_some() {
23234            // Hive format: STORED AS ...
23235            self.write_keyword("STORED AS");
23236            self.write_space();
23237            if let Some(this) = &e.this {
23238                // Uppercase the format name (e.g., parquet -> PARQUET)
23239                if let Expression::Identifier(id) = this.as_ref() {
23240                    self.write_keyword(&id.name.to_uppercase());
23241                } else {
23242                    self.generate_expression(this)?;
23243                }
23244            }
23245        } else if matches!(self.config.dialect, Some(DialectType::Hive)) {
23246            // Hive: STORED AS format
23247            self.write_keyword("STORED AS");
23248            self.write_space();
23249            if let Some(this) = &e.this {
23250                if let Expression::Identifier(id) = this.as_ref() {
23251                    self.write_keyword(&id.name.to_uppercase());
23252                } else {
23253                    self.generate_expression(this)?;
23254                }
23255            }
23256        } else if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks)) {
23257            // Spark/Databricks: USING format (e.g., USING DELTA)
23258            self.write_keyword("USING");
23259            self.write_space();
23260            if let Some(this) = &e.this {
23261                self.generate_expression(this)?;
23262            }
23263        } else {
23264            // Snowflake/standard format
23265            self.write_keyword("FILE_FORMAT");
23266            self.write(" = ");
23267            if let Some(this) = &e.this {
23268                self.generate_expression(this)?;
23269            } else if !e.expressions.is_empty() {
23270                self.write("(");
23271                for (i, expr) in e.expressions.iter().enumerate() {
23272                    if i > 0 {
23273                        self.write(", ");
23274                    }
23275                    self.generate_expression(expr)?;
23276                }
23277                self.write(")");
23278            }
23279        }
23280        Ok(())
23281    }
23282
23283    fn generate_filter(&mut self, e: &Filter) -> Result<()> {
23284        // agg_func FILTER(WHERE condition)
23285        self.generate_expression(&e.this)?;
23286        self.write_space();
23287        self.write_keyword("FILTER");
23288        self.write("(");
23289        self.write_keyword("WHERE");
23290        self.write_space();
23291        self.generate_expression(&e.expression)?;
23292        self.write(")");
23293        Ok(())
23294    }
23295
23296    fn generate_float64(&mut self, e: &Float64) -> Result<()> {
23297        // FLOAT64(this) or FLOAT64(this, expression)
23298        self.write_keyword("FLOAT64");
23299        self.write("(");
23300        self.generate_expression(&e.this)?;
23301        if let Some(expr) = &e.expression {
23302            self.write(", ");
23303            self.generate_expression(expr)?;
23304        }
23305        self.write(")");
23306        Ok(())
23307    }
23308
23309    fn generate_for_in(&mut self, e: &ForIn) -> Result<()> {
23310        // FOR this DO expression
23311        self.write_keyword("FOR");
23312        self.write_space();
23313        self.generate_expression(&e.this)?;
23314        self.write_space();
23315        self.write_keyword("DO");
23316        self.write_space();
23317        self.generate_expression(&e.expression)?;
23318        Ok(())
23319    }
23320
23321    fn generate_foreign_key(&mut self, e: &ForeignKey) -> Result<()> {
23322        // FOREIGN KEY (cols) REFERENCES table(cols) ON DELETE action ON UPDATE action
23323        self.write_keyword("FOREIGN KEY");
23324        if !e.expressions.is_empty() {
23325            self.write(" (");
23326            for (i, expr) in e.expressions.iter().enumerate() {
23327                if i > 0 {
23328                    self.write(", ");
23329                }
23330                self.generate_expression(expr)?;
23331            }
23332            self.write(")");
23333        }
23334        if let Some(reference) = &e.reference {
23335            self.write_space();
23336            self.generate_expression(reference)?;
23337        }
23338        if let Some(delete) = &e.delete {
23339            self.write_space();
23340            self.write_keyword("ON DELETE");
23341            self.write_space();
23342            self.generate_expression(delete)?;
23343        }
23344        if let Some(update) = &e.update {
23345            self.write_space();
23346            self.write_keyword("ON UPDATE");
23347            self.write_space();
23348            self.generate_expression(update)?;
23349        }
23350        if !e.options.is_empty() {
23351            self.write_space();
23352            for (i, opt) in e.options.iter().enumerate() {
23353                if i > 0 {
23354                    self.write_space();
23355                }
23356                self.generate_expression(opt)?;
23357            }
23358        }
23359        Ok(())
23360    }
23361
23362    fn generate_format(&mut self, e: &Format) -> Result<()> {
23363        // FORMAT(this, expressions...)
23364        self.write_keyword("FORMAT");
23365        self.write("(");
23366        self.generate_expression(&e.this)?;
23367        for expr in &e.expressions {
23368            self.write(", ");
23369            self.generate_expression(expr)?;
23370        }
23371        self.write(")");
23372        Ok(())
23373    }
23374
23375    fn generate_format_phrase(&mut self, e: &FormatPhrase) -> Result<()> {
23376        // Teradata: column (FORMAT 'format_string')
23377        self.generate_expression(&e.this)?;
23378        self.write(" (");
23379        self.write_keyword("FORMAT");
23380        self.write(" '");
23381        self.write(&e.format);
23382        self.write("')");
23383        Ok(())
23384    }
23385
23386    fn generate_freespace_property(&mut self, e: &FreespaceProperty) -> Result<()> {
23387        // Python: FREESPACE=this[PERCENT]
23388        self.write_keyword("FREESPACE");
23389        self.write("=");
23390        self.generate_expression(&e.this)?;
23391        if e.percent.is_some() {
23392            self.write_keyword(" PERCENT");
23393        }
23394        Ok(())
23395    }
23396
23397    fn generate_from(&mut self, e: &From) -> Result<()> {
23398        // Python: return f"{self.seg('FROM')} {self.sql(expression, 'this')}"
23399        self.write_keyword("FROM");
23400        self.write_space();
23401
23402        // BigQuery, Hive, Spark, Databricks, SQLite, and ClickHouse prefer explicit CROSS JOIN over comma syntax
23403        // But keep commas when TABLESAMPLE is present
23404        use crate::dialects::DialectType;
23405        let has_tablesample = e.expressions.iter().any(|expr| matches!(expr, Expression::TableSample(_)));
23406        let use_cross_join = !has_tablesample && matches!(
23407            self.config.dialect,
23408            Some(DialectType::BigQuery)
23409                | Some(DialectType::Hive)
23410                | Some(DialectType::Spark)
23411                | Some(DialectType::Databricks)
23412                | Some(DialectType::SQLite)
23413                | Some(DialectType::ClickHouse)
23414        );
23415
23416        // Snowflake wraps standalone VALUES in FROM clause with parentheses
23417        let wrap_values_in_parens = matches!(
23418            self.config.dialect,
23419            Some(DialectType::Snowflake)
23420        );
23421
23422        for (i, expr) in e.expressions.iter().enumerate() {
23423            if i > 0 {
23424                if use_cross_join {
23425                    self.write(" CROSS JOIN ");
23426                } else {
23427                    self.write(", ");
23428                }
23429            }
23430            if wrap_values_in_parens && matches!(expr, Expression::Values(_)) {
23431                self.write("(");
23432                self.generate_expression(expr)?;
23433                self.write(")");
23434            } else {
23435                self.generate_expression(expr)?;
23436            }
23437        }
23438        Ok(())
23439    }
23440
23441    fn generate_from_base(&mut self, e: &FromBase) -> Result<()> {
23442        // FROM_BASE(this, expression) - convert from base N
23443        self.write_keyword("FROM_BASE");
23444        self.write("(");
23445        self.generate_expression(&e.this)?;
23446        self.write(", ");
23447        self.generate_expression(&e.expression)?;
23448        self.write(")");
23449        Ok(())
23450    }
23451
23452    fn generate_from_time_zone(&mut self, e: &FromTimeZone) -> Result<()> {
23453        // this AT TIME ZONE zone AT TIME ZONE 'UTC'
23454        self.generate_expression(&e.this)?;
23455        if let Some(zone) = &e.zone {
23456            self.write_space();
23457            self.write_keyword("AT TIME ZONE");
23458            self.write_space();
23459            self.generate_expression(zone)?;
23460            self.write_space();
23461            self.write_keyword("AT TIME ZONE");
23462            self.write(" 'UTC'");
23463        }
23464        Ok(())
23465    }
23466
23467    fn generate_gap_fill(&mut self, e: &GapFill) -> Result<()> {
23468        // GAP_FILL(this, ts_column, bucket_width, ...)
23469        self.write_keyword("GAP_FILL");
23470        self.write("(");
23471        self.generate_expression(&e.this)?;
23472        if let Some(ts_column) = &e.ts_column {
23473            self.write(", ");
23474            self.generate_expression(ts_column)?;
23475        }
23476        if let Some(bucket_width) = &e.bucket_width {
23477            self.write(", ");
23478            self.generate_expression(bucket_width)?;
23479        }
23480        if let Some(partitioning_columns) = &e.partitioning_columns {
23481            self.write(", ");
23482            self.generate_expression(partitioning_columns)?;
23483        }
23484        if let Some(value_columns) = &e.value_columns {
23485            self.write(", ");
23486            self.generate_expression(value_columns)?;
23487        }
23488        self.write(")");
23489        Ok(())
23490    }
23491
23492    fn generate_generate_date_array(&mut self, e: &GenerateDateArray) -> Result<()> {
23493        // GENERATE_DATE_ARRAY(start, end, step)
23494        self.write_keyword("GENERATE_DATE_ARRAY");
23495        self.write("(");
23496        let mut first = true;
23497        if let Some(start) = &e.start {
23498            self.generate_expression(start)?;
23499            first = false;
23500        }
23501        if let Some(end) = &e.end {
23502            if !first {
23503                self.write(", ");
23504            }
23505            self.generate_expression(end)?;
23506            first = false;
23507        }
23508        if let Some(step) = &e.step {
23509            if !first {
23510                self.write(", ");
23511            }
23512            self.generate_expression(step)?;
23513        }
23514        self.write(")");
23515        Ok(())
23516    }
23517
23518    fn generate_generate_embedding(&mut self, e: &GenerateEmbedding) -> Result<()> {
23519        // ML.GENERATE_EMBEDDING(model, content, params)
23520        self.write_keyword("ML.GENERATE_EMBEDDING");
23521        self.write("(");
23522        self.generate_expression(&e.this)?;
23523        self.write(", ");
23524        self.generate_expression(&e.expression)?;
23525        if let Some(params) = &e.params_struct {
23526            self.write(", ");
23527            self.generate_expression(params)?;
23528        }
23529        self.write(")");
23530        Ok(())
23531    }
23532
23533    fn generate_generate_series(&mut self, e: &GenerateSeries) -> Result<()> {
23534        // GENERATE_SERIES(start, end, step)
23535        self.write_keyword("GENERATE_SERIES");
23536        self.write("(");
23537        let mut first = true;
23538        if let Some(start) = &e.start {
23539            self.generate_expression(start)?;
23540            first = false;
23541        }
23542        if let Some(end) = &e.end {
23543            if !first {
23544                self.write(", ");
23545            }
23546            self.generate_expression(end)?;
23547            first = false;
23548        }
23549        if let Some(step) = &e.step {
23550            if !first {
23551                self.write(", ");
23552            }
23553            self.generate_expression(step)?;
23554        }
23555        self.write(")");
23556        Ok(())
23557    }
23558
23559    fn generate_generate_timestamp_array(&mut self, e: &GenerateTimestampArray) -> Result<()> {
23560        // GENERATE_TIMESTAMP_ARRAY(start, end, step)
23561        self.write_keyword("GENERATE_TIMESTAMP_ARRAY");
23562        self.write("(");
23563        let mut first = true;
23564        if let Some(start) = &e.start {
23565            self.generate_expression(start)?;
23566            first = false;
23567        }
23568        if let Some(end) = &e.end {
23569            if !first {
23570                self.write(", ");
23571            }
23572            self.generate_expression(end)?;
23573            first = false;
23574        }
23575        if let Some(step) = &e.step {
23576            if !first {
23577                self.write(", ");
23578            }
23579            self.generate_expression(step)?;
23580        }
23581        self.write(")");
23582        Ok(())
23583    }
23584
23585    fn generate_generated_as_identity_column_constraint(&mut self, e: &GeneratedAsIdentityColumnConstraint) -> Result<()> {
23586        use crate::dialects::DialectType;
23587
23588        // For Snowflake, use AUTOINCREMENT START x INCREMENT y syntax
23589        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
23590            self.write_keyword("AUTOINCREMENT");
23591            if let Some(start) = &e.start {
23592                self.write_keyword(" START ");
23593                self.generate_expression(start)?;
23594            }
23595            if let Some(increment) = &e.increment {
23596                self.write_keyword(" INCREMENT ");
23597                self.generate_expression(increment)?;
23598            }
23599            return Ok(());
23600        }
23601
23602        // Python: GENERATED [ALWAYS|BY DEFAULT [ON NULL]] AS IDENTITY [(start, increment, ...)]
23603        self.write_keyword("GENERATED");
23604        if let Some(this) = &e.this {
23605            // Check if it's a truthy boolean expression
23606            if let Expression::Boolean(b) = this.as_ref() {
23607                if b.value {
23608                    self.write_keyword(" ALWAYS");
23609                } else {
23610                    self.write_keyword(" BY DEFAULT");
23611                    if e.on_null.is_some() {
23612                        self.write_keyword(" ON NULL");
23613                    }
23614                }
23615            } else {
23616                self.write_keyword(" ALWAYS");
23617            }
23618        }
23619        self.write_keyword(" AS IDENTITY");
23620        // Add sequence options if any
23621        let has_options = e.start.is_some() || e.increment.is_some() || e.minvalue.is_some() || e.maxvalue.is_some();
23622        if has_options {
23623            self.write(" (");
23624            let mut first = true;
23625            if let Some(start) = &e.start {
23626                self.write_keyword("START WITH ");
23627                self.generate_expression(start)?;
23628                first = false;
23629            }
23630            if let Some(increment) = &e.increment {
23631                if !first { self.write(" "); }
23632                self.write_keyword("INCREMENT BY ");
23633                self.generate_expression(increment)?;
23634                first = false;
23635            }
23636            if let Some(minvalue) = &e.minvalue {
23637                if !first { self.write(" "); }
23638                self.write_keyword("MINVALUE ");
23639                self.generate_expression(minvalue)?;
23640                first = false;
23641            }
23642            if let Some(maxvalue) = &e.maxvalue {
23643                if !first { self.write(" "); }
23644                self.write_keyword("MAXVALUE ");
23645                self.generate_expression(maxvalue)?;
23646            }
23647            self.write(")");
23648        }
23649        Ok(())
23650    }
23651
23652    fn generate_generated_as_row_column_constraint(&mut self, e: &GeneratedAsRowColumnConstraint) -> Result<()> {
23653        // Python: GENERATED ALWAYS AS ROW START|END [HIDDEN]
23654        self.write_keyword("GENERATED ALWAYS AS ROW ");
23655        if e.start.is_some() {
23656            self.write_keyword("START");
23657        } else {
23658            self.write_keyword("END");
23659        }
23660        if e.hidden.is_some() {
23661            self.write_keyword(" HIDDEN");
23662        }
23663        Ok(())
23664    }
23665
23666    fn generate_get(&mut self, e: &Get) -> Result<()> {
23667        // GET this target properties
23668        self.write_keyword("GET");
23669        self.write_space();
23670        self.generate_expression(&e.this)?;
23671        if let Some(target) = &e.target {
23672            self.write_space();
23673            self.generate_expression(target)?;
23674        }
23675        for prop in &e.properties {
23676            self.write_space();
23677            self.generate_expression(prop)?;
23678        }
23679        Ok(())
23680    }
23681
23682    fn generate_get_extract(&mut self, e: &GetExtract) -> Result<()> {
23683        // GetExtract generates bracket access: this[expression]
23684        self.generate_expression(&e.this)?;
23685        self.write("[");
23686        self.generate_expression(&e.expression)?;
23687        self.write("]");
23688        Ok(())
23689    }
23690
23691    fn generate_getbit(&mut self, e: &Getbit) -> Result<()> {
23692        // GETBIT(this, expression) or GET_BIT(this, expression)
23693        self.write_keyword("GETBIT");
23694        self.write("(");
23695        self.generate_expression(&e.this)?;
23696        self.write(", ");
23697        self.generate_expression(&e.expression)?;
23698        self.write(")");
23699        Ok(())
23700    }
23701
23702    fn generate_grant_principal(&mut self, e: &GrantPrincipal) -> Result<()> {
23703        // [ROLE|GROUP] name (e.g., "ROLE admin", "GROUP qa_users", or just "user1")
23704        if e.is_role {
23705            self.write_keyword("ROLE");
23706            self.write_space();
23707        } else if e.is_group {
23708            self.write_keyword("GROUP");
23709            self.write_space();
23710        }
23711        self.write(&e.name.name);
23712        Ok(())
23713    }
23714
23715    fn generate_grant_privilege(&mut self, e: &GrantPrivilege) -> Result<()> {
23716        // privilege(columns) or just privilege
23717        self.generate_expression(&e.this)?;
23718        if !e.expressions.is_empty() {
23719            self.write("(");
23720            for (i, expr) in e.expressions.iter().enumerate() {
23721                if i > 0 {
23722                    self.write(", ");
23723                }
23724                self.generate_expression(expr)?;
23725            }
23726            self.write(")");
23727        }
23728        Ok(())
23729    }
23730
23731    fn generate_group(&mut self, e: &Group) -> Result<()> {
23732        // Python handles GROUP BY ALL/DISTINCT modifiers and grouping expressions
23733        self.write_keyword("GROUP BY");
23734        // Handle ALL/DISTINCT modifier: Some(true) = ALL, Some(false) = DISTINCT
23735        match e.all {
23736            Some(true) => {
23737                self.write_space();
23738                self.write_keyword("ALL");
23739            }
23740            Some(false) => {
23741                self.write_space();
23742                self.write_keyword("DISTINCT");
23743            }
23744            None => {}
23745        }
23746        if !e.expressions.is_empty() {
23747            self.write_space();
23748            for (i, expr) in e.expressions.iter().enumerate() {
23749                if i > 0 {
23750                    self.write(", ");
23751                }
23752                self.generate_expression(expr)?;
23753            }
23754        }
23755        // Handle CUBE, ROLLUP, GROUPING SETS
23756        if let Some(cube) = &e.cube {
23757            if !e.expressions.is_empty() {
23758                self.write(", ");
23759            } else {
23760                self.write_space();
23761            }
23762            self.generate_expression(cube)?;
23763        }
23764        if let Some(rollup) = &e.rollup {
23765            if !e.expressions.is_empty() || e.cube.is_some() {
23766                self.write(", ");
23767            } else {
23768                self.write_space();
23769            }
23770            self.generate_expression(rollup)?;
23771        }
23772        if let Some(grouping_sets) = &e.grouping_sets {
23773            if !e.expressions.is_empty() || e.cube.is_some() || e.rollup.is_some() {
23774                self.write(", ");
23775            } else {
23776                self.write_space();
23777            }
23778            self.generate_expression(grouping_sets)?;
23779        }
23780        if let Some(totals) = &e.totals {
23781            self.write_space();
23782            self.write_keyword("WITH TOTALS");
23783            self.generate_expression(totals)?;
23784        }
23785        Ok(())
23786    }
23787
23788    fn generate_group_by(&mut self, e: &GroupBy) -> Result<()> {
23789        // GROUP BY expressions
23790        self.write_keyword("GROUP BY");
23791        // Handle ALL/DISTINCT modifier: Some(true) = ALL, Some(false) = DISTINCT
23792        match e.all {
23793            Some(true) => {
23794                self.write_space();
23795                self.write_keyword("ALL");
23796            }
23797            Some(false) => {
23798                self.write_space();
23799                self.write_keyword("DISTINCT");
23800            }
23801            None => {}
23802        }
23803
23804        // Check for trailing WITH CUBE or WITH ROLLUP (Hive/MySQL syntax)
23805        // These are represented as Cube/Rollup expressions with empty expressions at the end
23806        let mut trailing_cube = false;
23807        let mut trailing_rollup = false;
23808        let mut regular_expressions: Vec<&Expression> = Vec::new();
23809
23810        for expr in &e.expressions {
23811            match expr {
23812                Expression::Cube(c) if c.expressions.is_empty() => {
23813                    trailing_cube = true;
23814                }
23815                Expression::Rollup(r) if r.expressions.is_empty() => {
23816                    trailing_rollup = true;
23817                }
23818                _ => {
23819                    regular_expressions.push(expr);
23820                }
23821            }
23822        }
23823
23824        // In pretty mode, put columns on separate lines
23825        if self.config.pretty {
23826            self.write_newline();
23827            self.indent_level += 1;
23828            for (i, expr) in regular_expressions.iter().enumerate() {
23829                if i > 0 {
23830                    self.write(",");
23831                    self.write_newline();
23832                }
23833                self.write_indent();
23834                self.generate_expression(expr)?;
23835            }
23836            self.indent_level -= 1;
23837        } else {
23838            self.write_space();
23839            for (i, expr) in regular_expressions.iter().enumerate() {
23840                if i > 0 {
23841                    self.write(", ");
23842                }
23843                self.generate_expression(expr)?;
23844            }
23845        }
23846
23847        // Output trailing WITH CUBE or WITH ROLLUP
23848        if trailing_cube {
23849            self.write_space();
23850            self.write_keyword("WITH CUBE");
23851        } else if trailing_rollup {
23852            self.write_space();
23853            self.write_keyword("WITH ROLLUP");
23854        }
23855
23856        // ClickHouse: WITH TOTALS
23857        if e.totals {
23858            self.write_space();
23859            self.write_keyword("WITH TOTALS");
23860        }
23861
23862        Ok(())
23863    }
23864
23865    fn generate_grouping(&mut self, e: &Grouping) -> Result<()> {
23866        // GROUPING(col1, col2, ...)
23867        self.write_keyword("GROUPING");
23868        self.write("(");
23869        for (i, expr) in e.expressions.iter().enumerate() {
23870            if i > 0 {
23871                self.write(", ");
23872            }
23873            self.generate_expression(expr)?;
23874        }
23875        self.write(")");
23876        Ok(())
23877    }
23878
23879    fn generate_grouping_id(&mut self, e: &GroupingId) -> Result<()> {
23880        // GROUPING_ID(col1, col2, ...)
23881        self.write_keyword("GROUPING_ID");
23882        self.write("(");
23883        for (i, expr) in e.expressions.iter().enumerate() {
23884            if i > 0 {
23885                self.write(", ");
23886            }
23887            self.generate_expression(expr)?;
23888        }
23889        self.write(")");
23890        Ok(())
23891    }
23892
23893    fn generate_grouping_sets(&mut self, e: &GroupingSets) -> Result<()> {
23894        // Python: return f"GROUPING SETS {self.wrap(grouping_sets)}"
23895        self.write_keyword("GROUPING SETS");
23896        self.write(" (");
23897        for (i, expr) in e.expressions.iter().enumerate() {
23898            if i > 0 {
23899                self.write(", ");
23900            }
23901            self.generate_expression(expr)?;
23902        }
23903        self.write(")");
23904        Ok(())
23905    }
23906
23907    fn generate_hash_agg(&mut self, e: &HashAgg) -> Result<()> {
23908        // HASH_AGG(this, expressions...)
23909        self.write_keyword("HASH_AGG");
23910        self.write("(");
23911        self.generate_expression(&e.this)?;
23912        for expr in &e.expressions {
23913            self.write(", ");
23914            self.generate_expression(expr)?;
23915        }
23916        self.write(")");
23917        Ok(())
23918    }
23919
23920    fn generate_having(&mut self, e: &Having) -> Result<()> {
23921        // Python: return f"{self.seg('HAVING')}{self.sep()}{this}"
23922        self.write_keyword("HAVING");
23923        self.write_space();
23924        self.generate_expression(&e.this)?;
23925        Ok(())
23926    }
23927
23928    fn generate_having_max(&mut self, e: &HavingMax) -> Result<()> {
23929        // Python: this HAVING MAX|MIN expression
23930        self.generate_expression(&e.this)?;
23931        self.write_space();
23932        self.write_keyword("HAVING");
23933        self.write_space();
23934        if e.max.is_some() {
23935            self.write_keyword("MAX");
23936        } else {
23937            self.write_keyword("MIN");
23938        }
23939        self.write_space();
23940        self.generate_expression(&e.expression)?;
23941        Ok(())
23942    }
23943
23944    fn generate_heredoc(&mut self, e: &Heredoc) -> Result<()> {
23945        use crate::dialects::DialectType;
23946        // DuckDB: convert dollar-tagged strings to single-quoted
23947        if matches!(self.config.dialect, Some(DialectType::DuckDB)) {
23948            // Extract the string content and output as single-quoted
23949            if let Expression::Literal(Literal::String(ref s)) = *e.this {
23950                return self.generate_string_literal(s);
23951            }
23952        }
23953        // PostgreSQL: preserve dollar-quoting
23954        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
23955            self.write("$");
23956            if let Some(tag) = &e.tag {
23957                self.generate_expression(tag)?;
23958            }
23959            self.write("$");
23960            self.generate_expression(&e.this)?;
23961            self.write("$");
23962            if let Some(tag) = &e.tag {
23963                self.generate_expression(tag)?;
23964            }
23965            self.write("$");
23966            return Ok(());
23967        }
23968        // Default: output as dollar-tagged
23969        self.write("$");
23970        if let Some(tag) = &e.tag {
23971            self.generate_expression(tag)?;
23972        }
23973        self.write("$");
23974        self.generate_expression(&e.this)?;
23975        self.write("$");
23976        if let Some(tag) = &e.tag {
23977            self.generate_expression(tag)?;
23978        }
23979        self.write("$");
23980        Ok(())
23981    }
23982
23983    fn generate_hex_encode(&mut self, e: &HexEncode) -> Result<()> {
23984        // HEX_ENCODE(this)
23985        self.write_keyword("HEX_ENCODE");
23986        self.write("(");
23987        self.generate_expression(&e.this)?;
23988        self.write(")");
23989        Ok(())
23990    }
23991
23992    fn generate_historical_data(&mut self, e: &HistoricalData) -> Result<()> {
23993        // Python: this (kind => expression)
23994        // Write the keyword (AT/BEFORE/END) directly to avoid quoting it as a reserved word
23995        match e.this.as_ref() {
23996            Expression::Identifier(id) => self.write(&id.name),
23997            other => self.generate_expression(other)?,
23998        }
23999        self.write(" (");
24000        self.write(&e.kind);
24001        self.write(" => ");
24002        self.generate_expression(&e.expression)?;
24003        self.write(")");
24004        Ok(())
24005    }
24006
24007    fn generate_hll(&mut self, e: &Hll) -> Result<()> {
24008        // HLL(this, expressions...)
24009        self.write_keyword("HLL");
24010        self.write("(");
24011        self.generate_expression(&e.this)?;
24012        for expr in &e.expressions {
24013            self.write(", ");
24014            self.generate_expression(expr)?;
24015        }
24016        self.write(")");
24017        Ok(())
24018    }
24019
24020    fn generate_in_out_column_constraint(&mut self, e: &InOutColumnConstraint) -> Result<()> {
24021        // Python: IN|OUT|IN OUT
24022        if e.input_.is_some() && e.output.is_some() {
24023            self.write_keyword("IN OUT");
24024        } else if e.input_.is_some() {
24025            self.write_keyword("IN");
24026        } else if e.output.is_some() {
24027            self.write_keyword("OUT");
24028        }
24029        Ok(())
24030    }
24031
24032    fn generate_include_property(&mut self, e: &IncludeProperty) -> Result<()> {
24033        // Python: INCLUDE this [column_def] [AS alias]
24034        self.write_keyword("INCLUDE");
24035        self.write_space();
24036        self.generate_expression(&e.this)?;
24037        if let Some(column_def) = &e.column_def {
24038            self.write_space();
24039            self.generate_expression(column_def)?;
24040        }
24041        if let Some(alias) = &e.alias {
24042            self.write_space();
24043            self.write_keyword("AS");
24044            self.write_space();
24045            self.write(alias);
24046        }
24047        Ok(())
24048    }
24049
24050    fn generate_index(&mut self, e: &Index) -> Result<()> {
24051        // [UNIQUE] [PRIMARY] [AMP] INDEX [name] [ON table] (params)
24052        if e.unique {
24053            self.write_keyword("UNIQUE");
24054            self.write_space();
24055        }
24056        if e.primary.is_some() {
24057            self.write_keyword("PRIMARY");
24058            self.write_space();
24059        }
24060        if e.amp.is_some() {
24061            self.write_keyword("AMP");
24062            self.write_space();
24063        }
24064        if e.table.is_none() {
24065            self.write_keyword("INDEX");
24066            self.write_space();
24067        }
24068        if let Some(name) = &e.this {
24069            self.generate_expression(name)?;
24070            self.write_space();
24071        }
24072        if let Some(table) = &e.table {
24073            self.write_keyword("ON");
24074            self.write_space();
24075            self.generate_expression(table)?;
24076        }
24077        if !e.params.is_empty() {
24078            self.write("(");
24079            for (i, param) in e.params.iter().enumerate() {
24080                if i > 0 {
24081                    self.write(", ");
24082                }
24083                self.generate_expression(param)?;
24084            }
24085            self.write(")");
24086        }
24087        Ok(())
24088    }
24089
24090    fn generate_index_column_constraint(&mut self, e: &IndexColumnConstraint) -> Result<()> {
24091        // Python: kind INDEX [this] [USING index_type] (expressions) [options]
24092        if let Some(kind) = &e.kind {
24093            self.write(kind);
24094            self.write_space();
24095        }
24096        self.write_keyword("INDEX");
24097        if let Some(this) = &e.this {
24098            self.write_space();
24099            self.generate_expression(this)?;
24100        }
24101        if let Some(index_type) = &e.index_type {
24102            self.write_space();
24103            self.write_keyword("USING");
24104            self.write_space();
24105            self.generate_expression(index_type)?;
24106        }
24107        if !e.expressions.is_empty() {
24108            self.write(" (");
24109            for (i, expr) in e.expressions.iter().enumerate() {
24110                if i > 0 {
24111                    self.write(", ");
24112                }
24113                self.generate_expression(expr)?;
24114            }
24115            self.write(")");
24116        }
24117        for opt in &e.options {
24118            self.write_space();
24119            self.generate_expression(opt)?;
24120        }
24121        Ok(())
24122    }
24123
24124    fn generate_index_constraint_option(&mut self, e: &IndexConstraintOption) -> Result<()> {
24125        // Python: KEY_BLOCK_SIZE = x | USING x | WITH PARSER x | COMMENT x | visible | engine_attr | secondary_engine_attr
24126        if let Some(key_block_size) = &e.key_block_size {
24127            self.write_keyword("KEY_BLOCK_SIZE");
24128            self.write(" = ");
24129            self.generate_expression(key_block_size)?;
24130        } else if let Some(using) = &e.using {
24131            self.write_keyword("USING");
24132            self.write_space();
24133            self.generate_expression(using)?;
24134        } else if let Some(parser) = &e.parser {
24135            self.write_keyword("WITH PARSER");
24136            self.write_space();
24137            self.generate_expression(parser)?;
24138        } else if let Some(comment) = &e.comment {
24139            self.write_keyword("COMMENT");
24140            self.write_space();
24141            self.generate_expression(comment)?;
24142        } else if let Some(visible) = &e.visible {
24143            self.generate_expression(visible)?;
24144        } else if let Some(engine_attr) = &e.engine_attr {
24145            self.write_keyword("ENGINE_ATTRIBUTE");
24146            self.write(" = ");
24147            self.generate_expression(engine_attr)?;
24148        } else if let Some(secondary_engine_attr) = &e.secondary_engine_attr {
24149            self.write_keyword("SECONDARY_ENGINE_ATTRIBUTE");
24150            self.write(" = ");
24151            self.generate_expression(secondary_engine_attr)?;
24152        }
24153        Ok(())
24154    }
24155
24156    fn generate_index_parameters(&mut self, e: &IndexParameters) -> Result<()> {
24157        // Python: [USING using] (columns) [PARTITION BY partition_by] [where] [INCLUDE (include)] [WITH (with_storage)] [USING INDEX TABLESPACE tablespace]
24158        if let Some(using) = &e.using {
24159            self.write_keyword("USING");
24160            self.write_space();
24161            self.generate_expression(using)?;
24162        }
24163        if !e.columns.is_empty() {
24164            self.write("(");
24165            for (i, col) in e.columns.iter().enumerate() {
24166                if i > 0 {
24167                    self.write(", ");
24168                }
24169                self.generate_expression(col)?;
24170            }
24171            self.write(")");
24172        }
24173        if let Some(partition_by) = &e.partition_by {
24174            self.write_space();
24175            self.write_keyword("PARTITION BY");
24176            self.write_space();
24177            self.generate_expression(partition_by)?;
24178        }
24179        if let Some(where_) = &e.where_ {
24180            self.write_space();
24181            self.generate_expression(where_)?;
24182        }
24183        if let Some(include) = &e.include {
24184            self.write_space();
24185            self.write_keyword("INCLUDE");
24186            self.write(" (");
24187            self.generate_expression(include)?;
24188            self.write(")");
24189        }
24190        if let Some(with_storage) = &e.with_storage {
24191            self.write_space();
24192            self.write_keyword("WITH");
24193            self.write(" (");
24194            self.generate_expression(with_storage)?;
24195            self.write(")");
24196        }
24197        if let Some(tablespace) = &e.tablespace {
24198            self.write_space();
24199            self.write_keyword("USING INDEX TABLESPACE");
24200            self.write_space();
24201            self.generate_expression(tablespace)?;
24202        }
24203        Ok(())
24204    }
24205
24206    fn generate_index_table_hint(&mut self, e: &IndexTableHint) -> Result<()> {
24207        // Python: this INDEX [FOR target] (expressions)
24208        // Write hint type (USE/IGNORE/FORCE) as keyword, not through generate_expression
24209        // to avoid quoting reserved keywords like IGNORE, FORCE, JOIN
24210        if let Expression::Identifier(id) = &*e.this {
24211            self.write_keyword(&id.name);
24212        } else {
24213            self.generate_expression(&e.this)?;
24214        }
24215        self.write_space();
24216        self.write_keyword("INDEX");
24217        if let Some(target) = &e.target {
24218            self.write_space();
24219            self.write_keyword("FOR");
24220            self.write_space();
24221            if let Expression::Identifier(id) = &**target {
24222                self.write_keyword(&id.name);
24223            } else {
24224                self.generate_expression(target)?;
24225            }
24226        }
24227        // Always output parentheses (even if empty, e.g. USE INDEX ())
24228        self.write(" (");
24229        for (i, expr) in e.expressions.iter().enumerate() {
24230            if i > 0 {
24231                self.write(", ");
24232            }
24233            self.generate_expression(expr)?;
24234        }
24235        self.write(")");
24236        Ok(())
24237    }
24238
24239    fn generate_inherits_property(&mut self, e: &InheritsProperty) -> Result<()> {
24240        // INHERITS (table1, table2, ...)
24241        self.write_keyword("INHERITS");
24242        self.write(" (");
24243        for (i, expr) in e.expressions.iter().enumerate() {
24244            if i > 0 {
24245                self.write(", ");
24246            }
24247            self.generate_expression(expr)?;
24248        }
24249        self.write(")");
24250        Ok(())
24251    }
24252
24253    fn generate_input_model_property(&mut self, e: &InputModelProperty) -> Result<()> {
24254        // INPUT(model)
24255        self.write_keyword("INPUT");
24256        self.write("(");
24257        self.generate_expression(&e.this)?;
24258        self.write(")");
24259        Ok(())
24260    }
24261
24262    fn generate_input_output_format(&mut self, e: &InputOutputFormat) -> Result<()> {
24263        // Python: INPUTFORMAT input_format OUTPUTFORMAT output_format
24264        if let Some(input_format) = &e.input_format {
24265            self.write_keyword("INPUTFORMAT");
24266            self.write_space();
24267            self.generate_expression(input_format)?;
24268        }
24269        if let Some(output_format) = &e.output_format {
24270            if e.input_format.is_some() {
24271                self.write(" ");
24272            }
24273            self.write_keyword("OUTPUTFORMAT");
24274            self.write_space();
24275            self.generate_expression(output_format)?;
24276        }
24277        Ok(())
24278    }
24279
24280    fn generate_install(&mut self, e: &Install) -> Result<()> {
24281        // [FORCE] INSTALL extension [FROM source]
24282        if e.force.is_some() {
24283            self.write_keyword("FORCE");
24284            self.write_space();
24285        }
24286        self.write_keyword("INSTALL");
24287        self.write_space();
24288        self.generate_expression(&e.this)?;
24289        if let Some(from) = &e.from_ {
24290            self.write_space();
24291            self.write_keyword("FROM");
24292            self.write_space();
24293            self.generate_expression(from)?;
24294        }
24295        Ok(())
24296    }
24297
24298    fn generate_interval_op(&mut self, e: &IntervalOp) -> Result<()> {
24299        // INTERVAL 'expression' unit
24300        self.write_keyword("INTERVAL");
24301        self.write_space();
24302        // When a unit is specified and the expression is a number,
24303        self.generate_expression(&e.expression)?;
24304        if let Some(unit) = &e.unit {
24305            self.write_space();
24306            self.write(unit);
24307        }
24308        Ok(())
24309    }
24310
24311    fn generate_interval_span(&mut self, e: &IntervalSpan) -> Result<()> {
24312        // unit TO unit (e.g., HOUR TO SECOND)
24313        self.write(&format!("{:?}", e.this).to_uppercase());
24314        self.write_space();
24315        self.write_keyword("TO");
24316        self.write_space();
24317        self.write(&format!("{:?}", e.expression).to_uppercase());
24318        Ok(())
24319    }
24320
24321    fn generate_into_clause(&mut self, e: &IntoClause) -> Result<()> {
24322        // INTO [TEMPORARY|UNLOGGED] table
24323        self.write_keyword("INTO");
24324        if e.temporary {
24325            self.write_keyword(" TEMPORARY");
24326        }
24327        if e.unlogged.is_some() {
24328            self.write_keyword(" UNLOGGED");
24329        }
24330        if let Some(this) = &e.this {
24331            self.write_space();
24332            self.generate_expression(this)?;
24333        }
24334        if !e.expressions.is_empty() {
24335            self.write(" (");
24336            for (i, expr) in e.expressions.iter().enumerate() {
24337                if i > 0 {
24338                    self.write(", ");
24339                }
24340                self.generate_expression(expr)?;
24341            }
24342            self.write(")");
24343        }
24344        Ok(())
24345    }
24346
24347    fn generate_introducer(&mut self, e: &Introducer) -> Result<()> {
24348        // Python: this expression (e.g., _utf8 'string')
24349        self.generate_expression(&e.this)?;
24350        self.write_space();
24351        self.generate_expression(&e.expression)?;
24352        Ok(())
24353    }
24354
24355    fn generate_isolated_loading_property(&mut self, e: &IsolatedLoadingProperty) -> Result<()> {
24356        // Python: WITH [NO] [CONCURRENT] ISOLATED LOADING [target]
24357        self.write_keyword("WITH");
24358        if e.no.is_some() {
24359            self.write_keyword(" NO");
24360        }
24361        if e.concurrent.is_some() {
24362            self.write_keyword(" CONCURRENT");
24363        }
24364        self.write_keyword(" ISOLATED LOADING");
24365        if let Some(target) = &e.target {
24366            self.write_space();
24367            self.generate_expression(target)?;
24368        }
24369        Ok(())
24370    }
24371
24372    fn generate_json(&mut self, e: &JSON) -> Result<()> {
24373        // Python: JSON [this] [WITHOUT|WITH] [UNIQUE KEYS]
24374        self.write_keyword("JSON");
24375        if let Some(this) = &e.this {
24376            self.write_space();
24377            self.generate_expression(this)?;
24378        }
24379        if let Some(with_) = &e.with_ {
24380            // Check if it's a truthy boolean
24381            if let Expression::Boolean(b) = with_.as_ref() {
24382                if b.value {
24383                    self.write_keyword(" WITH");
24384                } else {
24385                    self.write_keyword(" WITHOUT");
24386                }
24387            }
24388        }
24389        if e.unique {
24390            self.write_keyword(" UNIQUE KEYS");
24391        }
24392        Ok(())
24393    }
24394
24395    fn generate_json_array(&mut self, e: &JSONArray) -> Result<()> {
24396        // Python: return self.func("JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})")
24397        self.write_keyword("JSON_ARRAY");
24398        self.write("(");
24399        for (i, expr) in e.expressions.iter().enumerate() {
24400            if i > 0 {
24401                self.write(", ");
24402            }
24403            self.generate_expression(expr)?;
24404        }
24405        if let Some(null_handling) = &e.null_handling {
24406            self.write_space();
24407            self.generate_expression(null_handling)?;
24408        }
24409        if let Some(return_type) = &e.return_type {
24410            self.write_space();
24411            self.write_keyword("RETURNING");
24412            self.write_space();
24413            self.generate_expression(return_type)?;
24414        }
24415        if e.strict.is_some() {
24416            self.write_space();
24417            self.write_keyword("STRICT");
24418        }
24419        self.write(")");
24420        Ok(())
24421    }
24422
24423    fn generate_json_array_agg_struct(&mut self, e: &JSONArrayAgg) -> Result<()> {
24424        // JSON_ARRAYAGG(this [ORDER BY ...] [NULL ON NULL | ABSENT ON NULL] [RETURNING type] [STRICT])
24425        self.write_keyword("JSON_ARRAYAGG");
24426        self.write("(");
24427        self.generate_expression(&e.this)?;
24428        if let Some(order) = &e.order {
24429            self.write_space();
24430            // Order is stored as an OrderBy expression
24431            if let Expression::OrderBy(ob) = order.as_ref() {
24432                self.write_keyword("ORDER BY");
24433                self.write_space();
24434                for (i, ord) in ob.expressions.iter().enumerate() {
24435                    if i > 0 {
24436                        self.write(", ");
24437                    }
24438                    self.generate_ordered(ord)?;
24439                }
24440            } else {
24441                // Fallback: generate the expression directly
24442                self.generate_expression(order)?;
24443            }
24444        }
24445        if let Some(null_handling) = &e.null_handling {
24446            self.write_space();
24447            self.generate_expression(null_handling)?;
24448        }
24449        if let Some(return_type) = &e.return_type {
24450            self.write_space();
24451            self.write_keyword("RETURNING");
24452            self.write_space();
24453            self.generate_expression(return_type)?;
24454        }
24455        if e.strict.is_some() {
24456            self.write_space();
24457            self.write_keyword("STRICT");
24458        }
24459        self.write(")");
24460        Ok(())
24461    }
24462
24463    fn generate_json_object_agg_struct(&mut self, e: &JSONObjectAgg) -> Result<()> {
24464        // JSON_OBJECTAGG(key: value [NULL ON NULL | ABSENT ON NULL] [WITH UNIQUE KEYS] [RETURNING type])
24465        self.write_keyword("JSON_OBJECTAGG");
24466        self.write("(");
24467        for (i, expr) in e.expressions.iter().enumerate() {
24468            if i > 0 {
24469                self.write(", ");
24470            }
24471            self.generate_expression(expr)?;
24472        }
24473        if let Some(null_handling) = &e.null_handling {
24474            self.write_space();
24475            self.generate_expression(null_handling)?;
24476        }
24477        if let Some(unique_keys) = &e.unique_keys {
24478            self.write_space();
24479            if let Expression::Boolean(b) = unique_keys.as_ref() {
24480                if b.value {
24481                    self.write_keyword("WITH UNIQUE KEYS");
24482                } else {
24483                    self.write_keyword("WITHOUT UNIQUE KEYS");
24484                }
24485            }
24486        }
24487        if let Some(return_type) = &e.return_type {
24488            self.write_space();
24489            self.write_keyword("RETURNING");
24490            self.write_space();
24491            self.generate_expression(return_type)?;
24492        }
24493        self.write(")");
24494        Ok(())
24495    }
24496
24497    fn generate_json_array_append(&mut self, e: &JSONArrayAppend) -> Result<()> {
24498        // JSON_ARRAY_APPEND(this, path, value, ...)
24499        self.write_keyword("JSON_ARRAY_APPEND");
24500        self.write("(");
24501        self.generate_expression(&e.this)?;
24502        for expr in &e.expressions {
24503            self.write(", ");
24504            self.generate_expression(expr)?;
24505        }
24506        self.write(")");
24507        Ok(())
24508    }
24509
24510    fn generate_json_array_contains(&mut self, e: &JSONArrayContains) -> Result<()> {
24511        // JSON_ARRAY_CONTAINS(this, expression)
24512        self.write_keyword("JSON_ARRAY_CONTAINS");
24513        self.write("(");
24514        self.generate_expression(&e.this)?;
24515        self.write(", ");
24516        self.generate_expression(&e.expression)?;
24517        self.write(")");
24518        Ok(())
24519    }
24520
24521    fn generate_json_array_insert(&mut self, e: &JSONArrayInsert) -> Result<()> {
24522        // JSON_ARRAY_INSERT(this, path, value, ...)
24523        self.write_keyword("JSON_ARRAY_INSERT");
24524        self.write("(");
24525        self.generate_expression(&e.this)?;
24526        for expr in &e.expressions {
24527            self.write(", ");
24528            self.generate_expression(expr)?;
24529        }
24530        self.write(")");
24531        Ok(())
24532    }
24533
24534    fn generate_jsonb_exists(&mut self, e: &JSONBExists) -> Result<()> {
24535        // JSONB_EXISTS(this, path)
24536        self.write_keyword("JSONB_EXISTS");
24537        self.write("(");
24538        self.generate_expression(&e.this)?;
24539        if let Some(path) = &e.path {
24540            self.write(", ");
24541            self.generate_expression(path)?;
24542        }
24543        self.write(")");
24544        Ok(())
24545    }
24546
24547    fn generate_jsonb_extract_scalar(&mut self, e: &JSONBExtractScalar) -> Result<()> {
24548        // JSONB_EXTRACT_SCALAR(this, expression)
24549        self.write_keyword("JSONB_EXTRACT_SCALAR");
24550        self.write("(");
24551        self.generate_expression(&e.this)?;
24552        self.write(", ");
24553        self.generate_expression(&e.expression)?;
24554        self.write(")");
24555        Ok(())
24556    }
24557
24558    fn generate_jsonb_object_agg(&mut self, e: &JSONBObjectAgg) -> Result<()> {
24559        // JSONB_OBJECT_AGG(this, expression)
24560        self.write_keyword("JSONB_OBJECT_AGG");
24561        self.write("(");
24562        self.generate_expression(&e.this)?;
24563        self.write(", ");
24564        self.generate_expression(&e.expression)?;
24565        self.write(")");
24566        Ok(())
24567    }
24568
24569    fn generate_json_column_def(&mut self, e: &JSONColumnDef) -> Result<()> {
24570        // Python: NESTED PATH path schema | this kind PATH path [FOR ORDINALITY]
24571        if let Some(nested_schema) = &e.nested_schema {
24572            self.write_keyword("NESTED");
24573            if let Some(path) = &e.path {
24574                self.write_space();
24575                self.write_keyword("PATH");
24576                self.write_space();
24577                self.generate_expression(path)?;
24578            }
24579            self.write_space();
24580            self.generate_expression(nested_schema)?;
24581        } else {
24582            if let Some(this) = &e.this {
24583                self.generate_expression(this)?;
24584            }
24585            if let Some(kind) = &e.kind {
24586                self.write_space();
24587                self.write(kind);
24588            }
24589            if let Some(path) = &e.path {
24590                self.write_space();
24591                self.write_keyword("PATH");
24592                self.write_space();
24593                self.generate_expression(path)?;
24594            }
24595            if e.ordinality.is_some() {
24596                self.write_keyword(" FOR ORDINALITY");
24597            }
24598        }
24599        Ok(())
24600    }
24601
24602    fn generate_json_exists(&mut self, e: &JSONExists) -> Result<()> {
24603        // JSON_EXISTS(this, path PASSING vars ON ERROR/EMPTY condition)
24604        self.write_keyword("JSON_EXISTS");
24605        self.write("(");
24606        self.generate_expression(&e.this)?;
24607        if let Some(path) = &e.path {
24608            self.write(", ");
24609            self.generate_expression(path)?;
24610        }
24611        if let Some(passing) = &e.passing {
24612            self.write_space();
24613            self.write_keyword("PASSING");
24614            self.write_space();
24615            self.generate_expression(passing)?;
24616        }
24617        if let Some(on_condition) = &e.on_condition {
24618            self.write_space();
24619            self.generate_expression(on_condition)?;
24620        }
24621        self.write(")");
24622        Ok(())
24623    }
24624
24625    fn generate_json_cast(&mut self, e: &JSONCast) -> Result<()> {
24626        self.generate_expression(&e.this)?;
24627        self.write(".:");
24628        self.generate_data_type(&e.to)?;
24629        Ok(())
24630    }
24631
24632    fn generate_json_extract_array(&mut self, e: &JSONExtractArray) -> Result<()> {
24633        // JSON_EXTRACT_ARRAY(this, expression)
24634        self.write_keyword("JSON_EXTRACT_ARRAY");
24635        self.write("(");
24636        self.generate_expression(&e.this)?;
24637        if let Some(expr) = &e.expression {
24638            self.write(", ");
24639            self.generate_expression(expr)?;
24640        }
24641        self.write(")");
24642        Ok(())
24643    }
24644
24645    fn generate_json_extract_quote(&mut self, e: &JSONExtractQuote) -> Result<()> {
24646        // Snowflake: KEEP [OMIT] QUOTES [SCALAR_ONLY] for JSON extraction
24647        if let Some(option) = &e.option {
24648            self.generate_expression(option)?;
24649            self.write_space();
24650        }
24651        self.write_keyword("QUOTES");
24652        if e.scalar.is_some() {
24653            self.write_keyword(" SCALAR_ONLY");
24654        }
24655        Ok(())
24656    }
24657
24658    fn generate_json_extract_scalar(&mut self, e: &JSONExtractScalar) -> Result<()> {
24659        // JSON_EXTRACT_SCALAR(this, expression)
24660        self.write_keyword("JSON_EXTRACT_SCALAR");
24661        self.write("(");
24662        self.generate_expression(&e.this)?;
24663        self.write(", ");
24664        self.generate_expression(&e.expression)?;
24665        self.write(")");
24666        Ok(())
24667    }
24668
24669    fn generate_json_extract_path(&mut self, e: &JSONExtract) -> Result<()> {
24670        // For variant_extract (Snowflake/Databricks colon syntax like a:field)
24671        // Databricks uses col:path syntax, Snowflake uses GET_PATH(col, 'path')
24672        // Otherwise output JSON_EXTRACT(this, expression)
24673        if e.variant_extract.is_some() {
24674            use crate::dialects::DialectType;
24675            if matches!(self.config.dialect, Some(DialectType::Databricks)) {
24676                // Databricks: output col:path syntax (e.g., c1:price, c1:price.foo, c1:price.bar[1])
24677                self.generate_expression(&e.this)?;
24678                self.write(":");
24679                // The expression is a string literal containing the path (e.g., 'price' or 'price.foo')
24680                // We need to output it without quotes
24681                match e.expression.as_ref() {
24682                    Expression::Literal(Literal::String(s)) => {
24683                        self.write(s);
24684                    }
24685                    _ => {
24686                        // Fallback: generate as-is (shouldn't happen in typical cases)
24687                        self.generate_expression(&e.expression)?;
24688                    }
24689                }
24690            } else {
24691                // Snowflake and others: use GET_PATH(col, 'path')
24692                self.write_keyword("GET_PATH");
24693                self.write("(");
24694                self.generate_expression(&e.this)?;
24695                self.write(", ");
24696                self.generate_expression(&e.expression)?;
24697                self.write(")");
24698            }
24699        } else {
24700            self.write_keyword("JSON_EXTRACT");
24701            self.write("(");
24702            self.generate_expression(&e.this)?;
24703            self.write(", ");
24704            self.generate_expression(&e.expression)?;
24705            for expr in &e.expressions {
24706                self.write(", ");
24707                self.generate_expression(expr)?;
24708            }
24709            self.write(")");
24710        }
24711        Ok(())
24712    }
24713
24714    fn generate_json_format(&mut self, e: &JSONFormat) -> Result<()> {
24715        // Output: {expr} FORMAT JSON
24716        // This wraps an expression with FORMAT JSON suffix (Oracle JSON function syntax)
24717        if let Some(this) = &e.this {
24718            self.generate_expression(this)?;
24719            self.write_space();
24720        }
24721        self.write_keyword("FORMAT JSON");
24722        Ok(())
24723    }
24724
24725    fn generate_json_key_value(&mut self, e: &JSONKeyValue) -> Result<()> {
24726        // key: value (for JSON objects)
24727        self.generate_expression(&e.this)?;
24728        self.write(": ");
24729        self.generate_expression(&e.expression)?;
24730        Ok(())
24731    }
24732
24733    fn generate_json_keys(&mut self, e: &JSONKeys) -> Result<()> {
24734        // JSON_KEYS(this, expression, expressions...)
24735        self.write_keyword("JSON_KEYS");
24736        self.write("(");
24737        self.generate_expression(&e.this)?;
24738        if let Some(expr) = &e.expression {
24739            self.write(", ");
24740            self.generate_expression(expr)?;
24741        }
24742        for expr in &e.expressions {
24743            self.write(", ");
24744            self.generate_expression(expr)?;
24745        }
24746        self.write(")");
24747        Ok(())
24748    }
24749
24750    fn generate_json_keys_at_depth(&mut self, e: &JSONKeysAtDepth) -> Result<()> {
24751        // JSON_KEYS(this, expression)
24752        self.write_keyword("JSON_KEYS");
24753        self.write("(");
24754        self.generate_expression(&e.this)?;
24755        if let Some(expr) = &e.expression {
24756            self.write(", ");
24757            self.generate_expression(expr)?;
24758        }
24759        self.write(")");
24760        Ok(())
24761    }
24762
24763    fn generate_json_path_expr(&mut self, e: &JSONPath) -> Result<()> {
24764        // JSONPath expression: generates a quoted path like '$.foo' or '$[0]'
24765        // The path components are concatenated without spaces
24766        let mut path_str = String::new();
24767        for expr in &e.expressions {
24768            match expr {
24769                Expression::JSONPathRoot(_) => {
24770                    path_str.push('$');
24771                }
24772                Expression::JSONPathKey(k) => {
24773                    // .key or ."key" (quote if key has special characters)
24774                    if let Expression::Literal(crate::expressions::Literal::String(s)) = k.this.as_ref() {
24775                        path_str.push('.');
24776                        // Quote the key if it contains non-alphanumeric characters (hyphens, spaces, etc.)
24777                        let needs_quoting = s.chars().any(|c| !c.is_alphanumeric() && c != '_');
24778                        if needs_quoting {
24779                            path_str.push('"');
24780                            path_str.push_str(s);
24781                            path_str.push('"');
24782                        } else {
24783                            path_str.push_str(s);
24784                        }
24785                    }
24786                }
24787                Expression::JSONPathSubscript(s) => {
24788                    // [index]
24789                    if let Expression::Literal(crate::expressions::Literal::Number(n)) = s.this.as_ref() {
24790                        path_str.push('[');
24791                        path_str.push_str(n);
24792                        path_str.push(']');
24793                    }
24794                }
24795                _ => {
24796                    // For other path parts, try to generate them
24797                    let mut temp_gen = Self::with_config(self.config.clone());
24798                    temp_gen.generate_expression(expr)?;
24799                    path_str.push_str(&temp_gen.output);
24800                }
24801            }
24802        }
24803        // Output as quoted string
24804        self.write("'");
24805        self.write(&path_str);
24806        self.write("'");
24807        Ok(())
24808    }
24809
24810    fn generate_json_path_filter(&mut self, e: &JSONPathFilter) -> Result<()> {
24811        // JSON path filter: ?(predicate)
24812        self.write("?(");
24813        self.generate_expression(&e.this)?;
24814        self.write(")");
24815        Ok(())
24816    }
24817
24818    fn generate_json_path_key(&mut self, e: &JSONPathKey) -> Result<()> {
24819        // JSON path key: .key or ["key"]
24820        self.write(".");
24821        self.generate_expression(&e.this)?;
24822        Ok(())
24823    }
24824
24825    fn generate_json_path_recursive(&mut self, e: &JSONPathRecursive) -> Result<()> {
24826        // JSON path recursive descent: ..
24827        self.write("..");
24828        if let Some(this) = &e.this {
24829            self.generate_expression(this)?;
24830        }
24831        Ok(())
24832    }
24833
24834    fn generate_json_path_root(&mut self) -> Result<()> {
24835        // JSON path root: $
24836        self.write("$");
24837        Ok(())
24838    }
24839
24840    fn generate_json_path_script(&mut self, e: &JSONPathScript) -> Result<()> {
24841        // JSON path script: (expression)
24842        self.write("(");
24843        self.generate_expression(&e.this)?;
24844        self.write(")");
24845        Ok(())
24846    }
24847
24848    fn generate_json_path_selector(&mut self, e: &JSONPathSelector) -> Result<()> {
24849        // JSON path selector: *
24850        self.generate_expression(&e.this)?;
24851        Ok(())
24852    }
24853
24854    fn generate_json_path_slice(&mut self, e: &JSONPathSlice) -> Result<()> {
24855        // JSON path slice: [start:end:step]
24856        self.write("[");
24857        if let Some(start) = &e.start {
24858            self.generate_expression(start)?;
24859        }
24860        self.write(":");
24861        if let Some(end) = &e.end {
24862            self.generate_expression(end)?;
24863        }
24864        if let Some(step) = &e.step {
24865            self.write(":");
24866            self.generate_expression(step)?;
24867        }
24868        self.write("]");
24869        Ok(())
24870    }
24871
24872    fn generate_json_path_subscript(&mut self, e: &JSONPathSubscript) -> Result<()> {
24873        // JSON path subscript: [index] or [*]
24874        self.write("[");
24875        self.generate_expression(&e.this)?;
24876        self.write("]");
24877        Ok(())
24878    }
24879
24880    fn generate_json_path_union(&mut self, e: &JSONPathUnion) -> Result<()> {
24881        // JSON path union: [key1, key2, ...]
24882        self.write("[");
24883        for (i, expr) in e.expressions.iter().enumerate() {
24884            if i > 0 {
24885                self.write(", ");
24886            }
24887            self.generate_expression(expr)?;
24888        }
24889        self.write("]");
24890        Ok(())
24891    }
24892
24893    fn generate_json_remove(&mut self, e: &JSONRemove) -> Result<()> {
24894        // JSON_REMOVE(this, path1, path2, ...)
24895        self.write_keyword("JSON_REMOVE");
24896        self.write("(");
24897        self.generate_expression(&e.this)?;
24898        for expr in &e.expressions {
24899            self.write(", ");
24900            self.generate_expression(expr)?;
24901        }
24902        self.write(")");
24903        Ok(())
24904    }
24905
24906    fn generate_json_schema(&mut self, e: &JSONSchema) -> Result<()> {
24907        // COLUMNS(col1 type, col2 type, ...)
24908        // When pretty printing and content is too wide, format with each column on a separate line
24909        self.write_keyword("COLUMNS");
24910        self.write("(");
24911
24912        if self.config.pretty && !e.expressions.is_empty() {
24913            // First, generate all expressions into strings to check width
24914            let mut expr_strings: Vec<String> = Vec::with_capacity(e.expressions.len());
24915            for expr in &e.expressions {
24916                let mut temp_gen = Generator::with_config(self.config.clone());
24917                temp_gen.generate_expression(expr)?;
24918                expr_strings.push(temp_gen.output);
24919            }
24920
24921            // Check if total width exceeds max_text_width
24922            if self.too_wide(&expr_strings) {
24923                // Pretty print: each column on its own line
24924                self.write_newline();
24925                self.indent_level += 1;
24926                for (i, expr_str) in expr_strings.iter().enumerate() {
24927                    if i > 0 {
24928                        self.write(",");
24929                        self.write_newline();
24930                    }
24931                    self.write_indent();
24932                    self.write(expr_str);
24933                }
24934                self.write_newline();
24935                self.indent_level -= 1;
24936                self.write_indent();
24937            } else {
24938                // Compact: all on one line
24939                for (i, expr_str) in expr_strings.iter().enumerate() {
24940                    if i > 0 {
24941                        self.write(", ");
24942                    }
24943                    self.write(expr_str);
24944                }
24945            }
24946        } else {
24947            // Non-pretty mode: compact format
24948            for (i, expr) in e.expressions.iter().enumerate() {
24949                if i > 0 {
24950                    self.write(", ");
24951                }
24952                self.generate_expression(expr)?;
24953            }
24954        }
24955        self.write(")");
24956        Ok(())
24957    }
24958
24959    fn generate_json_set(&mut self, e: &JSONSet) -> Result<()> {
24960        // JSON_SET(this, path, value, ...)
24961        self.write_keyword("JSON_SET");
24962        self.write("(");
24963        self.generate_expression(&e.this)?;
24964        for expr in &e.expressions {
24965            self.write(", ");
24966            self.generate_expression(expr)?;
24967        }
24968        self.write(")");
24969        Ok(())
24970    }
24971
24972    fn generate_json_strip_nulls(&mut self, e: &JSONStripNulls) -> Result<()> {
24973        // JSON_STRIP_NULLS(this, expression)
24974        self.write_keyword("JSON_STRIP_NULLS");
24975        self.write("(");
24976        self.generate_expression(&e.this)?;
24977        if let Some(expr) = &e.expression {
24978            self.write(", ");
24979            self.generate_expression(expr)?;
24980        }
24981        self.write(")");
24982        Ok(())
24983    }
24984
24985    fn generate_json_table(&mut self, e: &JSONTable) -> Result<()> {
24986        // JSON_TABLE(this, path [error_handling] [empty_handling] schema)
24987        self.write_keyword("JSON_TABLE");
24988        self.write("(");
24989        self.generate_expression(&e.this)?;
24990        if let Some(path) = &e.path {
24991            self.write(", ");
24992            self.generate_expression(path)?;
24993        }
24994        if let Some(error_handling) = &e.error_handling {
24995            self.write_space();
24996            self.generate_expression(error_handling)?;
24997        }
24998        if let Some(empty_handling) = &e.empty_handling {
24999            self.write_space();
25000            self.generate_expression(empty_handling)?;
25001        }
25002        if let Some(schema) = &e.schema {
25003            self.write_space();
25004            self.generate_expression(schema)?;
25005        }
25006        self.write(")");
25007        Ok(())
25008    }
25009
25010    fn generate_json_type(&mut self, e: &JSONType) -> Result<()> {
25011        // JSON_TYPE(this)
25012        self.write_keyword("JSON_TYPE");
25013        self.write("(");
25014        self.generate_expression(&e.this)?;
25015        self.write(")");
25016        Ok(())
25017    }
25018
25019    fn generate_json_value(&mut self, e: &JSONValue) -> Result<()> {
25020        // JSON_VALUE(this, path RETURNING type ON condition)
25021        self.write_keyword("JSON_VALUE");
25022        self.write("(");
25023        self.generate_expression(&e.this)?;
25024        if let Some(path) = &e.path {
25025            self.write(", ");
25026            self.generate_expression(path)?;
25027        }
25028        if let Some(returning) = &e.returning {
25029            self.write_space();
25030            self.write_keyword("RETURNING");
25031            self.write_space();
25032            self.generate_expression(returning)?;
25033        }
25034        if let Some(on_condition) = &e.on_condition {
25035            self.write_space();
25036            self.generate_expression(on_condition)?;
25037        }
25038        self.write(")");
25039        Ok(())
25040    }
25041
25042    fn generate_json_value_array(&mut self, e: &JSONValueArray) -> Result<()> {
25043        // JSON_VALUE_ARRAY(this)
25044        self.write_keyword("JSON_VALUE_ARRAY");
25045        self.write("(");
25046        self.generate_expression(&e.this)?;
25047        self.write(")");
25048        Ok(())
25049    }
25050
25051    fn generate_jarowinkler_similarity(&mut self, e: &JarowinklerSimilarity) -> Result<()> {
25052        // JAROWINKLER_SIMILARITY(str1, str2)
25053        self.write_keyword("JAROWINKLER_SIMILARITY");
25054        self.write("(");
25055        self.generate_expression(&e.this)?;
25056        self.write(", ");
25057        self.generate_expression(&e.expression)?;
25058        self.write(")");
25059        Ok(())
25060    }
25061
25062    fn generate_join_hint(&mut self, e: &JoinHint) -> Result<()> {
25063        // Python: this(expressions)
25064        self.generate_expression(&e.this)?;
25065        self.write("(");
25066        for (i, expr) in e.expressions.iter().enumerate() {
25067            if i > 0 {
25068                self.write(", ");
25069            }
25070            self.generate_expression(expr)?;
25071        }
25072        self.write(")");
25073        Ok(())
25074    }
25075
25076    fn generate_journal_property(&mut self, e: &JournalProperty) -> Result<()> {
25077        // Python: {no}{local}{dual}{before}{after}JOURNAL
25078        if e.no.is_some() {
25079            self.write_keyword("NO ");
25080        }
25081        if let Some(local) = &e.local {
25082            self.generate_expression(local)?;
25083            self.write_space();
25084        }
25085        if e.dual.is_some() {
25086            self.write_keyword("DUAL ");
25087        }
25088        if e.before.is_some() {
25089            self.write_keyword("BEFORE ");
25090        }
25091        if e.after.is_some() {
25092            self.write_keyword("AFTER ");
25093        }
25094        self.write_keyword("JOURNAL");
25095        Ok(())
25096    }
25097
25098    fn generate_language_property(&mut self, e: &LanguageProperty) -> Result<()> {
25099        // LANGUAGE language_name
25100        self.write_keyword("LANGUAGE");
25101        self.write_space();
25102        self.generate_expression(&e.this)?;
25103        Ok(())
25104    }
25105
25106    fn generate_lateral(&mut self, e: &Lateral) -> Result<()> {
25107        // Python: handles LATERAL VIEW (Hive/Spark) and regular LATERAL
25108        if e.view.is_some() {
25109            // LATERAL VIEW [OUTER] expression [alias] [AS columns]
25110            self.write_keyword("LATERAL VIEW");
25111            if e.outer.is_some() {
25112                self.write_space();
25113                self.write_keyword("OUTER");
25114            }
25115            self.write_space();
25116            self.generate_expression(&e.this)?;
25117            if let Some(alias) = &e.alias {
25118                self.write_space();
25119                self.write(alias);
25120            }
25121        } else {
25122            // LATERAL subquery/function [WITH ORDINALITY] [AS alias(columns)]
25123            self.write_keyword("LATERAL");
25124            self.write_space();
25125            self.generate_expression(&e.this)?;
25126            if e.ordinality.is_some() {
25127                self.write_space();
25128                self.write_keyword("WITH ORDINALITY");
25129            }
25130            if let Some(alias) = &e.alias {
25131                self.write_space();
25132                self.write_keyword("AS");
25133                self.write_space();
25134                self.write(alias);
25135                if !e.column_aliases.is_empty() {
25136                    self.write("(");
25137                    for (i, col) in e.column_aliases.iter().enumerate() {
25138                        if i > 0 {
25139                            self.write(", ");
25140                        }
25141                        self.write(col);
25142                    }
25143                    self.write(")");
25144                }
25145            }
25146        }
25147        Ok(())
25148    }
25149
25150    fn generate_like_property(&mut self, e: &LikeProperty) -> Result<()> {
25151        // Python: LIKE this [options]
25152        self.write_keyword("LIKE");
25153        self.write_space();
25154        self.generate_expression(&e.this)?;
25155        for expr in &e.expressions {
25156            self.write_space();
25157            self.generate_expression(expr)?;
25158        }
25159        Ok(())
25160    }
25161
25162    fn generate_limit(&mut self, e: &Limit) -> Result<()> {
25163        self.write_keyword("LIMIT");
25164        self.write_space();
25165        self.write_limit_expr(&e.this)?;
25166        if e.percent {
25167            self.write_space();
25168            self.write_keyword("PERCENT");
25169        }
25170        Ok(())
25171    }
25172
25173    fn generate_limit_options(&mut self, e: &LimitOptions) -> Result<()> {
25174        // Python: [PERCENT][ROWS][WITH TIES|ONLY]
25175        if e.percent.is_some() {
25176            self.write_keyword(" PERCENT");
25177        }
25178        if e.rows.is_some() {
25179            self.write_keyword(" ROWS");
25180        }
25181        if e.with_ties.is_some() {
25182            self.write_keyword(" WITH TIES");
25183        } else if e.rows.is_some() {
25184            self.write_keyword(" ONLY");
25185        }
25186        Ok(())
25187    }
25188
25189    fn generate_list(&mut self, e: &List) -> Result<()> {
25190        use crate::dialects::DialectType;
25191        let is_materialize = matches!(self.config.dialect, Some(DialectType::Materialize));
25192
25193        // Check if this is a subquery-based list (LIST(SELECT ...))
25194        if e.expressions.len() == 1 {
25195            if let Expression::Select(_) = &e.expressions[0] {
25196                self.write_keyword("LIST");
25197                self.write("(");
25198                self.generate_expression(&e.expressions[0])?;
25199                self.write(")");
25200                return Ok(());
25201            }
25202        }
25203
25204        // For Materialize, output as LIST[expr, expr, ...]
25205        if is_materialize {
25206            self.write_keyword("LIST");
25207            self.write("[");
25208            for (i, expr) in e.expressions.iter().enumerate() {
25209                if i > 0 {
25210                    self.write(", ");
25211                }
25212                self.generate_expression(expr)?;
25213            }
25214            self.write("]");
25215        } else {
25216            // For other dialects, output as LIST(expr, expr, ...)
25217            self.write_keyword("LIST");
25218            self.write("(");
25219            for (i, expr) in e.expressions.iter().enumerate() {
25220                if i > 0 {
25221                    self.write(", ");
25222                }
25223                self.generate_expression(expr)?;
25224            }
25225            self.write(")");
25226        }
25227        Ok(())
25228    }
25229
25230    fn generate_tomap(&mut self, e: &ToMap) -> Result<()> {
25231        // Check if this is a subquery-based map (MAP(SELECT ...))
25232        if let Expression::Select(_) = &*e.this {
25233            self.write_keyword("MAP");
25234            self.write("(");
25235            self.generate_expression(&e.this)?;
25236            self.write(")");
25237            return Ok(());
25238        }
25239
25240        let is_duckdb = matches!(self.config.dialect, Some(DialectType::DuckDB));
25241
25242        // For Struct-based map: DuckDB uses MAP {'key': value}, Materialize uses MAP['key' => value]
25243        self.write_keyword("MAP");
25244        if is_duckdb {
25245            self.write(" {");
25246        } else {
25247            self.write("[");
25248        }
25249        if let Expression::Struct(s) = &*e.this {
25250            for (i, (_, expr)) in s.fields.iter().enumerate() {
25251                if i > 0 {
25252                    self.write(", ");
25253                }
25254                if let Expression::PropertyEQ(op) = expr {
25255                    self.generate_expression(&op.left)?;
25256                    if is_duckdb {
25257                        self.write(": ");
25258                    } else {
25259                        self.write(" => ");
25260                    }
25261                    self.generate_expression(&op.right)?;
25262                } else {
25263                    self.generate_expression(expr)?;
25264                }
25265            }
25266        }
25267        if is_duckdb {
25268            self.write("}");
25269        } else {
25270            self.write("]");
25271        }
25272        Ok(())
25273    }
25274
25275    fn generate_localtime(&mut self, e: &Localtime) -> Result<()> {
25276        // Python: LOCALTIME or LOCALTIME(precision)
25277        self.write_keyword("LOCALTIME");
25278        if let Some(precision) = &e.this {
25279            self.write("(");
25280            self.generate_expression(precision)?;
25281            self.write(")");
25282        }
25283        Ok(())
25284    }
25285
25286    fn generate_localtimestamp(&mut self, e: &Localtimestamp) -> Result<()> {
25287        // Python: LOCALTIMESTAMP or LOCALTIMESTAMP(precision)
25288        self.write_keyword("LOCALTIMESTAMP");
25289        if let Some(precision) = &e.this {
25290            self.write("(");
25291            self.generate_expression(precision)?;
25292            self.write(")");
25293        }
25294        Ok(())
25295    }
25296
25297    fn generate_location_property(&mut self, e: &LocationProperty) -> Result<()> {
25298        // LOCATION 'path'
25299        self.write_keyword("LOCATION");
25300        self.write_space();
25301        self.generate_expression(&e.this)?;
25302        Ok(())
25303    }
25304
25305    fn generate_lock(&mut self, e: &Lock) -> Result<()> {
25306        // Python: FOR UPDATE|FOR SHARE [OF tables] [NOWAIT|WAIT n]
25307        if e.update.is_some() {
25308            if e.key.is_some() {
25309                self.write_keyword("FOR NO KEY UPDATE");
25310            } else {
25311                self.write_keyword("FOR UPDATE");
25312            }
25313        } else {
25314            if e.key.is_some() {
25315                self.write_keyword("FOR KEY SHARE");
25316            } else {
25317                self.write_keyword("FOR SHARE");
25318            }
25319        }
25320        if !e.expressions.is_empty() {
25321            self.write_keyword(" OF ");
25322            for (i, expr) in e.expressions.iter().enumerate() {
25323                if i > 0 {
25324                    self.write(", ");
25325                }
25326                self.generate_expression(expr)?;
25327            }
25328        }
25329        // Handle wait option following Python sqlglot convention:
25330        // - Boolean(true) -> NOWAIT
25331        // - Boolean(false) -> SKIP LOCKED
25332        // - Literal (number) -> WAIT n
25333        if let Some(wait) = &e.wait {
25334            match wait.as_ref() {
25335                Expression::Boolean(b) => {
25336                    if b.value {
25337                        self.write_keyword(" NOWAIT");
25338                    } else {
25339                        self.write_keyword(" SKIP LOCKED");
25340                    }
25341                }
25342                _ => {
25343                    // It's a literal (number), output WAIT n
25344                    self.write_keyword(" WAIT ");
25345                    self.generate_expression(wait)?;
25346                }
25347            }
25348        }
25349        Ok(())
25350    }
25351
25352    fn generate_lock_property(&mut self, e: &LockProperty) -> Result<()> {
25353        // LOCK property
25354        self.write_keyword("LOCK");
25355        self.write_space();
25356        self.generate_expression(&e.this)?;
25357        Ok(())
25358    }
25359
25360    fn generate_locking_property(&mut self, e: &LockingProperty) -> Result<()> {
25361        // Python: LOCKING kind [this] [for_or_in] lock_type [OVERRIDE]
25362        self.write_keyword("LOCKING");
25363        self.write_space();
25364        self.write(&e.kind);
25365        if let Some(this) = &e.this {
25366            self.write_space();
25367            self.generate_expression(this)?;
25368        }
25369        if let Some(for_or_in) = &e.for_or_in {
25370            self.write_space();
25371            self.generate_expression(for_or_in)?;
25372        }
25373        if let Some(lock_type) = &e.lock_type {
25374            self.write_space();
25375            self.generate_expression(lock_type)?;
25376        }
25377        if e.override_.is_some() {
25378            self.write_keyword(" OVERRIDE");
25379        }
25380        Ok(())
25381    }
25382
25383    fn generate_locking_statement(&mut self, e: &LockingStatement) -> Result<()> {
25384        // this expression
25385        self.generate_expression(&e.this)?;
25386        self.write_space();
25387        self.generate_expression(&e.expression)?;
25388        Ok(())
25389    }
25390
25391    fn generate_log_property(&mut self, e: &LogProperty) -> Result<()> {
25392        // [NO] LOG
25393        if e.no.is_some() {
25394            self.write_keyword("NO ");
25395        }
25396        self.write_keyword("LOG");
25397        Ok(())
25398    }
25399
25400    fn generate_md5_digest(&mut self, e: &MD5Digest) -> Result<()> {
25401        // MD5(this, expressions...)
25402        self.write_keyword("MD5");
25403        self.write("(");
25404        self.generate_expression(&e.this)?;
25405        for expr in &e.expressions {
25406            self.write(", ");
25407            self.generate_expression(expr)?;
25408        }
25409        self.write(")");
25410        Ok(())
25411    }
25412
25413    fn generate_ml_forecast(&mut self, e: &MLForecast) -> Result<()> {
25414        // ML.FORECAST(model, [params])
25415        self.write_keyword("ML.FORECAST");
25416        self.write("(");
25417        self.generate_expression(&e.this)?;
25418        if let Some(expression) = &e.expression {
25419            self.write(", ");
25420            self.generate_expression(expression)?;
25421        }
25422        if let Some(params) = &e.params_struct {
25423            self.write(", ");
25424            self.generate_expression(params)?;
25425        }
25426        self.write(")");
25427        Ok(())
25428    }
25429
25430    fn generate_ml_translate(&mut self, e: &MLTranslate) -> Result<()> {
25431        // ML.TRANSLATE(model, input, [params])
25432        self.write_keyword("ML.TRANSLATE");
25433        self.write("(");
25434        self.generate_expression(&e.this)?;
25435        self.write(", ");
25436        self.generate_expression(&e.expression)?;
25437        if let Some(params) = &e.params_struct {
25438            self.write(", ");
25439            self.generate_expression(params)?;
25440        }
25441        self.write(")");
25442        Ok(())
25443    }
25444
25445    fn generate_make_interval(&mut self, e: &MakeInterval) -> Result<()> {
25446        // MAKE_INTERVAL(years => x, months => y, ...)
25447        self.write_keyword("MAKE_INTERVAL");
25448        self.write("(");
25449        let mut first = true;
25450        if let Some(year) = &e.year {
25451            self.write("years => ");
25452            self.generate_expression(year)?;
25453            first = false;
25454        }
25455        if let Some(month) = &e.month {
25456            if !first { self.write(", "); }
25457            self.write("months => ");
25458            self.generate_expression(month)?;
25459            first = false;
25460        }
25461        if let Some(week) = &e.week {
25462            if !first { self.write(", "); }
25463            self.write("weeks => ");
25464            self.generate_expression(week)?;
25465            first = false;
25466        }
25467        if let Some(day) = &e.day {
25468            if !first { self.write(", "); }
25469            self.write("days => ");
25470            self.generate_expression(day)?;
25471            first = false;
25472        }
25473        if let Some(hour) = &e.hour {
25474            if !first { self.write(", "); }
25475            self.write("hours => ");
25476            self.generate_expression(hour)?;
25477            first = false;
25478        }
25479        if let Some(minute) = &e.minute {
25480            if !first { self.write(", "); }
25481            self.write("mins => ");
25482            self.generate_expression(minute)?;
25483            first = false;
25484        }
25485        if let Some(second) = &e.second {
25486            if !first { self.write(", "); }
25487            self.write("secs => ");
25488            self.generate_expression(second)?;
25489        }
25490        self.write(")");
25491        Ok(())
25492    }
25493
25494    fn generate_manhattan_distance(&mut self, e: &ManhattanDistance) -> Result<()> {
25495        // MANHATTAN_DISTANCE(vector1, vector2)
25496        self.write_keyword("MANHATTAN_DISTANCE");
25497        self.write("(");
25498        self.generate_expression(&e.this)?;
25499        self.write(", ");
25500        self.generate_expression(&e.expression)?;
25501        self.write(")");
25502        Ok(())
25503    }
25504
25505    fn generate_map(&mut self, e: &Map) -> Result<()> {
25506        // MAP(key1, value1, key2, value2, ...)
25507        self.write_keyword("MAP");
25508        self.write("(");
25509        for (i, (key, value)) in e.keys.iter().zip(e.values.iter()).enumerate() {
25510            if i > 0 {
25511                self.write(", ");
25512            }
25513            self.generate_expression(key)?;
25514            self.write(", ");
25515            self.generate_expression(value)?;
25516        }
25517        self.write(")");
25518        Ok(())
25519    }
25520
25521    fn generate_map_cat(&mut self, e: &MapCat) -> Result<()> {
25522        // MAP_CAT(map1, map2)
25523        self.write_keyword("MAP_CAT");
25524        self.write("(");
25525        self.generate_expression(&e.this)?;
25526        self.write(", ");
25527        self.generate_expression(&e.expression)?;
25528        self.write(")");
25529        Ok(())
25530    }
25531
25532    fn generate_map_delete(&mut self, e: &MapDelete) -> Result<()> {
25533        // MAP_DELETE(map, key1, key2, ...)
25534        self.write_keyword("MAP_DELETE");
25535        self.write("(");
25536        self.generate_expression(&e.this)?;
25537        for expr in &e.expressions {
25538            self.write(", ");
25539            self.generate_expression(expr)?;
25540        }
25541        self.write(")");
25542        Ok(())
25543    }
25544
25545    fn generate_map_insert(&mut self, e: &MapInsert) -> Result<()> {
25546        // MAP_INSERT(map, key, value, [update_flag])
25547        self.write_keyword("MAP_INSERT");
25548        self.write("(");
25549        self.generate_expression(&e.this)?;
25550        if let Some(key) = &e.key {
25551            self.write(", ");
25552            self.generate_expression(key)?;
25553        }
25554        if let Some(value) = &e.value {
25555            self.write(", ");
25556            self.generate_expression(value)?;
25557        }
25558        if let Some(update_flag) = &e.update_flag {
25559            self.write(", ");
25560            self.generate_expression(update_flag)?;
25561        }
25562        self.write(")");
25563        Ok(())
25564    }
25565
25566    fn generate_map_pick(&mut self, e: &MapPick) -> Result<()> {
25567        // MAP_PICK(map, key1, key2, ...)
25568        self.write_keyword("MAP_PICK");
25569        self.write("(");
25570        self.generate_expression(&e.this)?;
25571        for expr in &e.expressions {
25572            self.write(", ");
25573            self.generate_expression(expr)?;
25574        }
25575        self.write(")");
25576        Ok(())
25577    }
25578
25579    fn generate_masking_policy_column_constraint(&mut self, e: &MaskingPolicyColumnConstraint) -> Result<()> {
25580        // Python: MASKING POLICY name [USING (cols)]
25581        self.write_keyword("MASKING POLICY");
25582        self.write_space();
25583        self.generate_expression(&e.this)?;
25584        if !e.expressions.is_empty() {
25585            self.write_keyword(" USING");
25586            self.write(" (");
25587            for (i, expr) in e.expressions.iter().enumerate() {
25588                if i > 0 {
25589                    self.write(", ");
25590                }
25591                self.generate_expression(expr)?;
25592            }
25593            self.write(")");
25594        }
25595        Ok(())
25596    }
25597
25598    fn generate_match_against(&mut self, e: &MatchAgainst) -> Result<()> {
25599        if matches!(
25600            self.config.dialect,
25601            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)
25602        ) {
25603            if e.expressions.len() > 1 {
25604                self.write("(");
25605            }
25606            for (i, expr) in e.expressions.iter().enumerate() {
25607                if i > 0 {
25608                    self.write_keyword(" OR ");
25609                }
25610                self.generate_expression(expr)?;
25611                self.write_space();
25612                self.write("@@");
25613                self.write_space();
25614                self.generate_expression(&e.this)?;
25615            }
25616            if e.expressions.len() > 1 {
25617                self.write(")");
25618            }
25619            return Ok(());
25620        }
25621
25622        // MATCH(columns) AGAINST(expr [modifier])
25623        self.write_keyword("MATCH");
25624        self.write("(");
25625        for (i, expr) in e.expressions.iter().enumerate() {
25626            if i > 0 {
25627                self.write(", ");
25628            }
25629            self.generate_expression(expr)?;
25630        }
25631        self.write(")");
25632        self.write_keyword(" AGAINST");
25633        self.write("(");
25634        self.generate_expression(&e.this)?;
25635        if let Some(modifier) = &e.modifier {
25636            self.write_space();
25637            self.generate_expression(modifier)?;
25638        }
25639        self.write(")");
25640        Ok(())
25641    }
25642
25643    fn generate_match_recognize_measure(&mut self, e: &MatchRecognizeMeasure) -> Result<()> {
25644        // Python: [window_frame] this
25645        if let Some(window_frame) = &e.window_frame {
25646            self.write(&format!("{:?}", window_frame).to_uppercase());
25647            self.write_space();
25648        }
25649        self.generate_expression(&e.this)?;
25650        Ok(())
25651    }
25652
25653    fn generate_materialized_property(&mut self, e: &MaterializedProperty) -> Result<()> {
25654        // MATERIALIZED [this]
25655        self.write_keyword("MATERIALIZED");
25656        if let Some(this) = &e.this {
25657            self.write_space();
25658            self.generate_expression(this)?;
25659        }
25660        Ok(())
25661    }
25662
25663    fn generate_merge(&mut self, e: &Merge) -> Result<()> {
25664        // MERGE INTO target USING source ON condition WHEN ...
25665        // DuckDB variant: MERGE INTO target USING source USING (key_columns) WHEN ...
25666        if let Some(with_) = &e.with_ {
25667            self.generate_expression(with_)?;
25668            self.write_space();
25669        }
25670        self.write_keyword("MERGE INTO");
25671        self.write_space();
25672        self.generate_expression(&e.this)?;
25673
25674        // USING clause - newline before in pretty mode
25675        if self.config.pretty {
25676            self.write_newline();
25677            self.write_indent();
25678        } else {
25679            self.write_space();
25680        }
25681        self.write_keyword("USING");
25682        self.write_space();
25683        self.generate_expression(&e.using)?;
25684
25685        // ON clause - newline before in pretty mode
25686        if let Some(on) = &e.on {
25687            if self.config.pretty {
25688                self.write_newline();
25689                self.write_indent();
25690            } else {
25691                self.write_space();
25692            }
25693            self.write_keyword("ON");
25694            self.write_space();
25695            self.generate_expression(on)?;
25696        }
25697        // DuckDB USING (key_columns) clause
25698        if let Some(using_cond) = &e.using_cond {
25699            self.write_space();
25700            self.write_keyword("USING");
25701            self.write_space();
25702            self.write("(");
25703            // using_cond is a Tuple containing the column identifiers
25704            if let Expression::Tuple(tuple) = using_cond.as_ref() {
25705                for (i, col) in tuple.expressions.iter().enumerate() {
25706                    if i > 0 {
25707                        self.write(", ");
25708                    }
25709                    self.generate_expression(col)?;
25710                }
25711            } else {
25712                self.generate_expression(using_cond)?;
25713            }
25714            self.write(")");
25715        }
25716        // For PostgreSQL dialect, extract target table name/alias to strip from UPDATE SET
25717        let saved_merge_strip = std::mem::take(&mut self.merge_strip_qualifiers);
25718        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)) {
25719            let mut names = Vec::new();
25720            match e.this.as_ref() {
25721                Expression::Alias(a) => {
25722                    // e.g., "x AS z" -> strip both "x" and "z"
25723                    if let Expression::Table(t) = &a.this {
25724                        names.push(t.name.name.clone());
25725                    } else if let Expression::Identifier(id) = &a.this {
25726                        names.push(id.name.clone());
25727                    }
25728                    names.push(a.alias.name.clone());
25729                }
25730                Expression::Table(t) => {
25731                    names.push(t.name.name.clone());
25732                }
25733                Expression::Identifier(id) => {
25734                    names.push(id.name.clone());
25735                }
25736                _ => {}
25737            }
25738            self.merge_strip_qualifiers = names;
25739        }
25740
25741        // WHEN clauses - newline before each in pretty mode
25742        if let Some(whens) = &e.whens {
25743            if self.config.pretty {
25744                self.write_newline();
25745                self.write_indent();
25746            } else {
25747                self.write_space();
25748            }
25749            self.generate_expression(whens)?;
25750        }
25751
25752        // Restore merge_strip_qualifiers
25753        self.merge_strip_qualifiers = saved_merge_strip;
25754
25755        // OUTPUT/RETURNING clause - newline before in pretty mode
25756        if let Some(returning) = &e.returning {
25757            if self.config.pretty {
25758                self.write_newline();
25759                self.write_indent();
25760            } else {
25761                self.write_space();
25762            }
25763            self.generate_expression(returning)?;
25764        }
25765        Ok(())
25766    }
25767
25768    fn generate_merge_block_ratio_property(&mut self, e: &MergeBlockRatioProperty) -> Result<()> {
25769        // Python: NO MERGEBLOCKRATIO | DEFAULT MERGEBLOCKRATIO | MERGEBLOCKRATIO=this [PERCENT]
25770        if e.no.is_some() {
25771            self.write_keyword("NO MERGEBLOCKRATIO");
25772        } else if e.default.is_some() {
25773            self.write_keyword("DEFAULT MERGEBLOCKRATIO");
25774        } else {
25775            self.write_keyword("MERGEBLOCKRATIO");
25776            self.write("=");
25777            if let Some(this) = &e.this {
25778                self.generate_expression(this)?;
25779            }
25780            if e.percent.is_some() {
25781                self.write_keyword(" PERCENT");
25782            }
25783        }
25784        Ok(())
25785    }
25786
25787    fn generate_merge_tree_ttl(&mut self, e: &MergeTreeTTL) -> Result<()> {
25788        // TTL expressions [WHERE where] [GROUP BY group] [SET aggregates]
25789        self.write_keyword("TTL");
25790        let pretty_clickhouse = self.config.pretty
25791            && matches!(self.config.dialect, Some(crate::dialects::DialectType::ClickHouse));
25792
25793        if pretty_clickhouse {
25794            self.write_newline();
25795            self.indent_level += 1;
25796            for (i, expr) in e.expressions.iter().enumerate() {
25797                if i > 0 {
25798                    self.write(",");
25799                    self.write_newline();
25800                }
25801                self.write_indent();
25802                self.generate_expression(expr)?;
25803            }
25804            self.indent_level -= 1;
25805        } else {
25806            self.write_space();
25807            for (i, expr) in e.expressions.iter().enumerate() {
25808                if i > 0 {
25809                    self.write(", ");
25810                }
25811                self.generate_expression(expr)?;
25812            }
25813        }
25814
25815        if let Some(where_) = &e.where_ {
25816            if pretty_clickhouse {
25817                self.write_newline();
25818                if let Expression::Where(w) = where_.as_ref() {
25819                    self.write_indent();
25820                    self.write_keyword("WHERE");
25821                    self.write_newline();
25822                    self.indent_level += 1;
25823                    self.write_indent();
25824                    self.generate_expression(&w.this)?;
25825                    self.indent_level -= 1;
25826                } else {
25827                    self.write_indent();
25828                    self.generate_expression(where_)?;
25829                }
25830            } else {
25831                self.write_space();
25832                self.generate_expression(where_)?;
25833            }
25834        }
25835        if let Some(group) = &e.group {
25836            if pretty_clickhouse {
25837                self.write_newline();
25838                if let Expression::Group(g) = group.as_ref() {
25839                    self.write_indent();
25840                    self.write_keyword("GROUP BY");
25841                    self.write_newline();
25842                    self.indent_level += 1;
25843                    for (i, expr) in g.expressions.iter().enumerate() {
25844                        if i > 0 {
25845                            self.write(",");
25846                            self.write_newline();
25847                        }
25848                        self.write_indent();
25849                        self.generate_expression(expr)?;
25850                    }
25851                    self.indent_level -= 1;
25852                } else {
25853                    self.write_indent();
25854                    self.generate_expression(group)?;
25855                }
25856            } else {
25857                self.write_space();
25858                self.generate_expression(group)?;
25859            }
25860        }
25861        if let Some(aggregates) = &e.aggregates {
25862            if pretty_clickhouse {
25863                self.write_newline();
25864                self.write_indent();
25865                self.write_keyword("SET");
25866                self.write_newline();
25867                self.indent_level += 1;
25868                if let Expression::Tuple(t) = aggregates.as_ref() {
25869                    for (i, agg) in t.expressions.iter().enumerate() {
25870                        if i > 0 {
25871                            self.write(",");
25872                            self.write_newline();
25873                        }
25874                        self.write_indent();
25875                        self.generate_expression(agg)?;
25876                    }
25877                } else {
25878                    self.write_indent();
25879                    self.generate_expression(aggregates)?;
25880                }
25881                self.indent_level -= 1;
25882            } else {
25883                self.write_space();
25884                self.write_keyword("SET");
25885                self.write_space();
25886                self.generate_expression(aggregates)?;
25887            }
25888        }
25889        Ok(())
25890    }
25891
25892    fn generate_merge_tree_ttl_action(&mut self, e: &MergeTreeTTLAction) -> Result<()> {
25893        // Python: this [DELETE] [RECOMPRESS codec] [TO DISK disk] [TO VOLUME volume]
25894        self.generate_expression(&e.this)?;
25895        if e.delete.is_some() {
25896            self.write_keyword(" DELETE");
25897        }
25898        if let Some(recompress) = &e.recompress {
25899            self.write_keyword(" RECOMPRESS ");
25900            self.generate_expression(recompress)?;
25901        }
25902        if let Some(to_disk) = &e.to_disk {
25903            self.write_keyword(" TO DISK ");
25904            self.generate_expression(to_disk)?;
25905        }
25906        if let Some(to_volume) = &e.to_volume {
25907            self.write_keyword(" TO VOLUME ");
25908            self.generate_expression(to_volume)?;
25909        }
25910        Ok(())
25911    }
25912
25913    fn generate_minhash(&mut self, e: &Minhash) -> Result<()> {
25914        // MINHASH(this, expressions...)
25915        self.write_keyword("MINHASH");
25916        self.write("(");
25917        self.generate_expression(&e.this)?;
25918        for expr in &e.expressions {
25919            self.write(", ");
25920            self.generate_expression(expr)?;
25921        }
25922        self.write(")");
25923        Ok(())
25924    }
25925
25926    fn generate_model_attribute(&mut self, e: &ModelAttribute) -> Result<()> {
25927        // model!attribute - Snowflake syntax
25928        self.generate_expression(&e.this)?;
25929        self.write("!");
25930        self.generate_expression(&e.expression)?;
25931        Ok(())
25932    }
25933
25934    fn generate_monthname(&mut self, e: &Monthname) -> Result<()> {
25935        // MONTHNAME(this)
25936        self.write_keyword("MONTHNAME");
25937        self.write("(");
25938        self.generate_expression(&e.this)?;
25939        self.write(")");
25940        Ok(())
25941    }
25942
25943    fn generate_multitable_inserts(&mut self, e: &MultitableInserts) -> Result<()> {
25944        // Output leading comments
25945        for comment in &e.leading_comments {
25946            self.write_formatted_comment(comment);
25947            self.write_space();
25948        }
25949        // Python: INSERT kind expressions source
25950        self.write_keyword("INSERT");
25951        self.write_space();
25952        self.write(&e.kind);
25953        for expr in &e.expressions {
25954            self.write_space();
25955            self.generate_expression(expr)?;
25956        }
25957        if let Some(source) = &e.source {
25958            self.write_space();
25959            self.generate_expression(source)?;
25960        }
25961        Ok(())
25962    }
25963
25964    fn generate_next_value_for(&mut self, e: &NextValueFor) -> Result<()> {
25965        // Python: NEXT VALUE FOR this [OVER (order)]
25966        self.write_keyword("NEXT VALUE FOR");
25967        self.write_space();
25968        self.generate_expression(&e.this)?;
25969        if let Some(order) = &e.order {
25970            self.write_space();
25971            self.write_keyword("OVER");
25972            self.write(" (");
25973            self.generate_expression(order)?;
25974            self.write(")");
25975        }
25976        Ok(())
25977    }
25978
25979    fn generate_normal(&mut self, e: &Normal) -> Result<()> {
25980        // NORMAL(mean, stddev, gen)
25981        self.write_keyword("NORMAL");
25982        self.write("(");
25983        self.generate_expression(&e.this)?;
25984        if let Some(stddev) = &e.stddev {
25985            self.write(", ");
25986            self.generate_expression(stddev)?;
25987        }
25988        if let Some(gen) = &e.gen {
25989            self.write(", ");
25990            self.generate_expression(gen)?;
25991        }
25992        self.write(")");
25993        Ok(())
25994    }
25995
25996    fn generate_normalize(&mut self, e: &Normalize) -> Result<()> {
25997        // NORMALIZE(this, form) or CASEFOLD version
25998        if e.is_casefold.is_some() {
25999            self.write_keyword("NORMALIZE_AND_CASEFOLD");
26000        } else {
26001            self.write_keyword("NORMALIZE");
26002        }
26003        self.write("(");
26004        self.generate_expression(&e.this)?;
26005        if let Some(form) = &e.form {
26006            self.write(", ");
26007            self.generate_expression(form)?;
26008        }
26009        self.write(")");
26010        Ok(())
26011    }
26012
26013    fn generate_not_null_column_constraint(&mut self, e: &NotNullColumnConstraint) -> Result<()> {
26014        // Python: [NOT ]NULL
26015        if e.allow_null.is_none() {
26016            self.write_keyword("NOT ");
26017        }
26018        self.write_keyword("NULL");
26019        Ok(())
26020    }
26021
26022    fn generate_nullif(&mut self, e: &Nullif) -> Result<()> {
26023        // NULLIF(this, expression)
26024        self.write_keyword("NULLIF");
26025        self.write("(");
26026        self.generate_expression(&e.this)?;
26027        self.write(", ");
26028        self.generate_expression(&e.expression)?;
26029        self.write(")");
26030        Ok(())
26031    }
26032
26033    fn generate_number_to_str(&mut self, e: &NumberToStr) -> Result<()> {
26034        // FORMAT(this, format, culture)
26035        self.write_keyword("FORMAT");
26036        self.write("(");
26037        self.generate_expression(&e.this)?;
26038        self.write(", '");
26039        self.write(&e.format);
26040        self.write("'");
26041        if let Some(culture) = &e.culture {
26042            self.write(", ");
26043            self.generate_expression(culture)?;
26044        }
26045        self.write(")");
26046        Ok(())
26047    }
26048
26049    fn generate_object_agg(&mut self, e: &ObjectAgg) -> Result<()> {
26050        // OBJECT_AGG(key, value)
26051        self.write_keyword("OBJECT_AGG");
26052        self.write("(");
26053        self.generate_expression(&e.this)?;
26054        self.write(", ");
26055        self.generate_expression(&e.expression)?;
26056        self.write(")");
26057        Ok(())
26058    }
26059
26060    fn generate_object_identifier(&mut self, e: &ObjectIdentifier) -> Result<()> {
26061        // Python: Just returns the name
26062        self.generate_expression(&e.this)?;
26063        Ok(())
26064    }
26065
26066    fn generate_object_insert(&mut self, e: &ObjectInsert) -> Result<()> {
26067        // OBJECT_INSERT(obj, key, value, [update_flag])
26068        self.write_keyword("OBJECT_INSERT");
26069        self.write("(");
26070        self.generate_expression(&e.this)?;
26071        if let Some(key) = &e.key {
26072            self.write(", ");
26073            self.generate_expression(key)?;
26074        }
26075        if let Some(value) = &e.value {
26076            self.write(", ");
26077            self.generate_expression(value)?;
26078        }
26079        if let Some(update_flag) = &e.update_flag {
26080            self.write(", ");
26081            self.generate_expression(update_flag)?;
26082        }
26083        self.write(")");
26084        Ok(())
26085    }
26086
26087    fn generate_offset(&mut self, e: &Offset) -> Result<()> {
26088        // OFFSET value [ROW|ROWS]
26089        self.write_keyword("OFFSET");
26090        self.write_space();
26091        self.generate_expression(&e.this)?;
26092        // Output ROWS keyword only for TSQL/Oracle targets
26093        if e.rows == Some(true) && matches!(self.config.dialect, Some(crate::dialects::DialectType::TSQL) | Some(crate::dialects::DialectType::Oracle)) {
26094            self.write_space();
26095            self.write_keyword("ROWS");
26096        }
26097        Ok(())
26098    }
26099
26100    fn generate_qualify(&mut self, e: &Qualify) -> Result<()> {
26101        // QUALIFY condition (Snowflake/BigQuery)
26102        self.write_keyword("QUALIFY");
26103        self.write_space();
26104        self.generate_expression(&e.this)?;
26105        Ok(())
26106    }
26107
26108    fn generate_on_cluster(&mut self, e: &OnCluster) -> Result<()> {
26109        // ON CLUSTER cluster_name
26110        self.write_keyword("ON CLUSTER");
26111        self.write_space();
26112        self.generate_expression(&e.this)?;
26113        Ok(())
26114    }
26115
26116    fn generate_on_commit_property(&mut self, e: &OnCommitProperty) -> Result<()> {
26117        // ON COMMIT [DELETE ROWS | PRESERVE ROWS]
26118        self.write_keyword("ON COMMIT");
26119        if e.delete.is_some() {
26120            self.write_keyword(" DELETE ROWS");
26121        } else {
26122            self.write_keyword(" PRESERVE ROWS");
26123        }
26124        Ok(())
26125    }
26126
26127    fn generate_on_condition(&mut self, e: &OnCondition) -> Result<()> {
26128        // Python: error/empty/null handling
26129        if let Some(empty) = &e.empty {
26130            self.generate_expression(empty)?;
26131            self.write_keyword(" ON EMPTY");
26132        }
26133        if let Some(error) = &e.error {
26134            if e.empty.is_some() {
26135                self.write_space();
26136            }
26137            self.generate_expression(error)?;
26138            self.write_keyword(" ON ERROR");
26139        }
26140        if let Some(null) = &e.null {
26141            if e.empty.is_some() || e.error.is_some() {
26142                self.write_space();
26143            }
26144            self.generate_expression(null)?;
26145            self.write_keyword(" ON NULL");
26146        }
26147        Ok(())
26148    }
26149
26150    fn generate_on_conflict(&mut self, e: &OnConflict) -> Result<()> {
26151        // Materialize doesn't support ON CONFLICT - skip entirely
26152        if matches!(self.config.dialect, Some(DialectType::Materialize)) {
26153            return Ok(());
26154        }
26155        // Python: ON CONFLICT|ON DUPLICATE KEY [ON CONSTRAINT constraint] [conflict_keys] action
26156        if e.duplicate.is_some() {
26157            // MySQL: ON DUPLICATE KEY UPDATE col = val, ...
26158            self.write_keyword("ON DUPLICATE KEY UPDATE");
26159            for (i, expr) in e.expressions.iter().enumerate() {
26160                if i > 0 {
26161                    self.write(",");
26162                }
26163                self.write_space();
26164                self.generate_expression(expr)?;
26165            }
26166            return Ok(());
26167        } else {
26168            self.write_keyword("ON CONFLICT");
26169        }
26170        if let Some(constraint) = &e.constraint {
26171            self.write_keyword(" ON CONSTRAINT ");
26172            self.generate_expression(constraint)?;
26173        }
26174        if let Some(conflict_keys) = &e.conflict_keys {
26175            // conflict_keys can be a Tuple containing expressions
26176            if let Expression::Tuple(t) = conflict_keys.as_ref() {
26177                self.write("(");
26178                for (i, expr) in t.expressions.iter().enumerate() {
26179                    if i > 0 {
26180                        self.write(", ");
26181                    }
26182                    self.generate_expression(expr)?;
26183                }
26184                self.write(")");
26185            } else {
26186                self.write("(");
26187                self.generate_expression(conflict_keys)?;
26188                self.write(")");
26189            }
26190        }
26191        if let Some(index_predicate) = &e.index_predicate {
26192            self.write_keyword(" WHERE ");
26193            self.generate_expression(index_predicate)?;
26194        }
26195        if let Some(action) = &e.action {
26196            // Check if action is "NOTHING" or an UPDATE set
26197            if let Expression::Identifier(id) = action.as_ref() {
26198                if id.name == "NOTHING" || id.name.to_uppercase() == "NOTHING" {
26199                    self.write_keyword(" DO NOTHING");
26200                } else {
26201                    self.write_keyword(" DO ");
26202                    self.generate_expression(action)?;
26203                }
26204            } else if let Expression::Tuple(t) = action.as_ref() {
26205                // DO UPDATE SET col1 = val1, col2 = val2
26206                self.write_keyword(" DO UPDATE SET ");
26207                for (i, expr) in t.expressions.iter().enumerate() {
26208                    if i > 0 {
26209                        self.write(", ");
26210                    }
26211                    self.generate_expression(expr)?;
26212                }
26213            } else {
26214                self.write_keyword(" DO ");
26215                self.generate_expression(action)?;
26216            }
26217        }
26218        // WHERE clause for the UPDATE action
26219        if let Some(where_) = &e.where_ {
26220            self.write_keyword(" WHERE ");
26221            self.generate_expression(where_)?;
26222        }
26223        Ok(())
26224    }
26225
26226    fn generate_on_property(&mut self, e: &OnProperty) -> Result<()> {
26227        // ON property_value
26228        self.write_keyword("ON");
26229        self.write_space();
26230        self.generate_expression(&e.this)?;
26231        Ok(())
26232    }
26233
26234    fn generate_opclass(&mut self, e: &Opclass) -> Result<()> {
26235        // Python: this expression (e.g., column opclass)
26236        self.generate_expression(&e.this)?;
26237        self.write_space();
26238        self.generate_expression(&e.expression)?;
26239        Ok(())
26240    }
26241
26242    fn generate_open_json(&mut self, e: &OpenJSON) -> Result<()> {
26243        // Python: OPENJSON(this[, path]) [WITH (columns)]
26244        self.write_keyword("OPENJSON");
26245        self.write("(");
26246        self.generate_expression(&e.this)?;
26247        if let Some(path) = &e.path {
26248            self.write(", ");
26249            self.generate_expression(path)?;
26250        }
26251        self.write(")");
26252        if !e.expressions.is_empty() {
26253            self.write_keyword(" WITH");
26254            if self.config.pretty {
26255                self.write(" (\n");
26256                self.indent_level += 2;
26257                for (i, expr) in e.expressions.iter().enumerate() {
26258                    if i > 0 {
26259                        self.write(",\n");
26260                    }
26261                    self.write_indent();
26262                    self.generate_expression(expr)?;
26263                }
26264                self.write("\n");
26265                self.indent_level -= 2;
26266                self.write(")");
26267            } else {
26268                self.write(" (");
26269                for (i, expr) in e.expressions.iter().enumerate() {
26270                    if i > 0 {
26271                        self.write(", ");
26272                    }
26273                    self.generate_expression(expr)?;
26274                }
26275                self.write(")");
26276            }
26277        }
26278        Ok(())
26279    }
26280
26281    fn generate_open_json_column_def(&mut self, e: &OpenJSONColumnDef) -> Result<()> {
26282        // Python: this kind [path] [AS JSON]
26283        self.generate_expression(&e.this)?;
26284        self.write_space();
26285        // Use parsed data_type if available, otherwise fall back to kind string
26286        if let Some(ref dt) = e.data_type {
26287            self.generate_data_type(dt)?;
26288        } else if !e.kind.is_empty() {
26289            self.write(&e.kind);
26290        }
26291        if let Some(path) = &e.path {
26292            self.write_space();
26293            self.generate_expression(path)?;
26294        }
26295        if e.as_json.is_some() {
26296            self.write_keyword(" AS JSON");
26297        }
26298        Ok(())
26299    }
26300
26301    fn generate_operator(&mut self, e: &Operator) -> Result<()> {
26302        // this OPERATOR(op) expression
26303        self.generate_expression(&e.this)?;
26304        self.write_space();
26305        if let Some(op) = &e.operator {
26306            self.write_keyword("OPERATOR");
26307            self.write("(");
26308            self.generate_expression(op)?;
26309            self.write(")");
26310        }
26311        self.write_space();
26312        self.generate_expression(&e.expression)?;
26313        Ok(())
26314    }
26315
26316    fn generate_order_by(&mut self, e: &OrderBy) -> Result<()> {
26317        // ORDER BY expr1 [ASC|DESC] [NULLS FIRST|LAST], expr2 ...
26318        self.write_keyword("ORDER BY");
26319        let pretty_clickhouse_single_paren = self.config.pretty
26320            && matches!(self.config.dialect, Some(DialectType::ClickHouse))
26321            && e.expressions.len() == 1
26322            && matches!(e.expressions[0].this, Expression::Paren(ref p) if !matches!(p.this, Expression::Tuple(_)));
26323        let clickhouse_single_tuple = matches!(self.config.dialect, Some(DialectType::ClickHouse))
26324            && e.expressions.len() == 1
26325            && matches!(e.expressions[0].this, Expression::Tuple(_))
26326            && !e.expressions[0].desc
26327            && e.expressions[0].nulls_first.is_none();
26328
26329        if pretty_clickhouse_single_paren {
26330            self.write_space();
26331            if let Expression::Paren(p) = &e.expressions[0].this {
26332                self.write("(");
26333                self.write_newline();
26334                self.indent_level += 1;
26335                self.write_indent();
26336                self.generate_expression(&p.this)?;
26337                self.indent_level -= 1;
26338                self.write_newline();
26339                self.write(")");
26340            }
26341            return Ok(());
26342        }
26343
26344        if clickhouse_single_tuple {
26345            self.write_space();
26346            if let Expression::Tuple(t) = &e.expressions[0].this {
26347                self.write("(");
26348                for (i, expr) in t.expressions.iter().enumerate() {
26349                    if i > 0 {
26350                        self.write(", ");
26351                    }
26352                    self.generate_expression(expr)?;
26353                }
26354                self.write(")");
26355            }
26356            return Ok(());
26357        }
26358
26359        self.write_space();
26360        for (i, ordered) in e.expressions.iter().enumerate() {
26361            if i > 0 {
26362                self.write(", ");
26363            }
26364            self.generate_expression(&ordered.this)?;
26365            if ordered.desc {
26366                self.write_space();
26367                self.write_keyword("DESC");
26368            } else if ordered.explicit_asc {
26369                self.write_space();
26370                self.write_keyword("ASC");
26371            }
26372            if let Some(nulls_first) = ordered.nulls_first {
26373                // In Dremio, NULLS LAST is the default, so skip generating it
26374                let skip_nulls_last = !nulls_first
26375                    && matches!(self.config.dialect, Some(DialectType::Dremio));
26376                if !skip_nulls_last {
26377                    self.write_space();
26378                    self.write_keyword("NULLS");
26379                    self.write_space();
26380                    if nulls_first {
26381                        self.write_keyword("FIRST");
26382                    } else {
26383                        self.write_keyword("LAST");
26384                    }
26385                }
26386            }
26387        }
26388        Ok(())
26389    }
26390
26391    fn generate_output_model_property(&mut self, e: &OutputModelProperty) -> Result<()> {
26392        // OUTPUT(model)
26393        self.write_keyword("OUTPUT");
26394        self.write("(");
26395        if self.config.pretty {
26396            self.indent_level += 1;
26397            self.write_newline();
26398            self.write_indent();
26399            self.generate_expression(&e.this)?;
26400            self.indent_level -= 1;
26401            self.write_newline();
26402        } else {
26403            self.generate_expression(&e.this)?;
26404        }
26405        self.write(")");
26406        Ok(())
26407    }
26408
26409    fn generate_overflow_truncate_behavior(&mut self, e: &OverflowTruncateBehavior) -> Result<()> {
26410        // Python: TRUNCATE [filler] WITH|WITHOUT COUNT
26411        self.write_keyword("TRUNCATE");
26412        if let Some(this) = &e.this {
26413            self.write_space();
26414            self.generate_expression(this)?;
26415        }
26416        if e.with_count.is_some() {
26417            self.write_keyword(" WITH COUNT");
26418        } else {
26419            self.write_keyword(" WITHOUT COUNT");
26420        }
26421        Ok(())
26422    }
26423
26424    fn generate_parameterized_agg(&mut self, e: &ParameterizedAgg) -> Result<()> {
26425        // Python: name(expressions)(params)
26426        self.generate_expression(&e.this)?;
26427        self.write("(");
26428        for (i, expr) in e.expressions.iter().enumerate() {
26429            if i > 0 {
26430                self.write(", ");
26431            }
26432            self.generate_expression(expr)?;
26433        }
26434        self.write(")(");
26435        for (i, param) in e.params.iter().enumerate() {
26436            if i > 0 {
26437                self.write(", ");
26438            }
26439            self.generate_expression(param)?;
26440        }
26441        self.write(")");
26442        Ok(())
26443    }
26444
26445    fn generate_parse_datetime(&mut self, e: &ParseDatetime) -> Result<()> {
26446        // PARSE_DATETIME(format, this) or similar
26447        self.write_keyword("PARSE_DATETIME");
26448        self.write("(");
26449        if let Some(format) = &e.format {
26450            self.write("'");
26451            self.write(format);
26452            self.write("', ");
26453        }
26454        self.generate_expression(&e.this)?;
26455        if let Some(zone) = &e.zone {
26456            self.write(", ");
26457            self.generate_expression(zone)?;
26458        }
26459        self.write(")");
26460        Ok(())
26461    }
26462
26463    fn generate_parse_ip(&mut self, e: &ParseIp) -> Result<()> {
26464        // PARSE_IP(this, type, permissive)
26465        self.write_keyword("PARSE_IP");
26466        self.write("(");
26467        self.generate_expression(&e.this)?;
26468        if let Some(type_) = &e.type_ {
26469            self.write(", ");
26470            self.generate_expression(type_)?;
26471        }
26472        if let Some(permissive) = &e.permissive {
26473            self.write(", ");
26474            self.generate_expression(permissive)?;
26475        }
26476        self.write(")");
26477        Ok(())
26478    }
26479
26480    fn generate_parse_json(&mut self, e: &ParseJSON) -> Result<()> {
26481        // PARSE_JSON(this, [expression])
26482        self.write_keyword("PARSE_JSON");
26483        self.write("(");
26484        self.generate_expression(&e.this)?;
26485        if let Some(expression) = &e.expression {
26486            self.write(", ");
26487            self.generate_expression(expression)?;
26488        }
26489        self.write(")");
26490        Ok(())
26491    }
26492
26493    fn generate_parse_time(&mut self, e: &ParseTime) -> Result<()> {
26494        // PARSE_TIME(format, this) or STR_TO_TIME(this, format)
26495        self.write_keyword("PARSE_TIME");
26496        self.write("(");
26497        self.write(&format!("'{}'", e.format));
26498        self.write(", ");
26499        self.generate_expression(&e.this)?;
26500        self.write(")");
26501        Ok(())
26502    }
26503
26504    fn generate_parse_url(&mut self, e: &ParseUrl) -> Result<()> {
26505        // PARSE_URL(this, [part_to_extract], [key], [permissive])
26506        self.write_keyword("PARSE_URL");
26507        self.write("(");
26508        self.generate_expression(&e.this)?;
26509        if let Some(part) = &e.part_to_extract {
26510            self.write(", ");
26511            self.generate_expression(part)?;
26512        }
26513        if let Some(key) = &e.key {
26514            self.write(", ");
26515            self.generate_expression(key)?;
26516        }
26517        if let Some(permissive) = &e.permissive {
26518            self.write(", ");
26519            self.generate_expression(permissive)?;
26520        }
26521        self.write(")");
26522        Ok(())
26523    }
26524
26525    fn generate_partition_expr(&mut self, e: &Partition) -> Result<()> {
26526        // PARTITION(expr1, expr2, ...) or SUBPARTITION(expr1, expr2, ...)
26527        if e.subpartition {
26528            self.write_keyword("SUBPARTITION");
26529        } else {
26530            self.write_keyword("PARTITION");
26531        }
26532        self.write("(");
26533        for (i, expr) in e.expressions.iter().enumerate() {
26534            if i > 0 {
26535                self.write(", ");
26536            }
26537            self.generate_expression(expr)?;
26538        }
26539        self.write(")");
26540        Ok(())
26541    }
26542
26543    fn generate_partition_bound_spec(&mut self, e: &PartitionBoundSpec) -> Result<()> {
26544        // IN (values) or WITH (MODULUS this, REMAINDER expression) or FROM (from) TO (to)
26545        if let Some(this) = &e.this {
26546            if let Some(expression) = &e.expression {
26547                // WITH (MODULUS this, REMAINDER expression)
26548                self.write_keyword("WITH");
26549                self.write(" (");
26550                self.write_keyword("MODULUS");
26551                self.write_space();
26552                self.generate_expression(this)?;
26553                self.write(", ");
26554                self.write_keyword("REMAINDER");
26555                self.write_space();
26556                self.generate_expression(expression)?;
26557                self.write(")");
26558            } else {
26559                // IN (this) - this could be a list
26560                self.write_keyword("IN");
26561                self.write(" (");
26562                self.generate_partition_bound_values(this)?;
26563                self.write(")");
26564            }
26565        } else if let (Some(from), Some(to)) = (&e.from_expressions, &e.to_expressions) {
26566            // FROM (from_expressions) TO (to_expressions)
26567            self.write_keyword("FROM");
26568            self.write(" (");
26569            self.generate_partition_bound_values(from)?;
26570            self.write(") ");
26571            self.write_keyword("TO");
26572            self.write(" (");
26573            self.generate_partition_bound_values(to)?;
26574            self.write(")");
26575        }
26576        Ok(())
26577    }
26578
26579    /// Generate partition bound values - handles Tuple expressions by outputting
26580    /// contents without wrapping parens (since caller provides the parens)
26581    fn generate_partition_bound_values(&mut self, expr: &Expression) -> Result<()> {
26582        if let Expression::Tuple(t) = expr {
26583            for (i, e) in t.expressions.iter().enumerate() {
26584                if i > 0 {
26585                    self.write(", ");
26586                }
26587                self.generate_expression(e)?;
26588            }
26589            Ok(())
26590        } else {
26591            self.generate_expression(expr)
26592        }
26593    }
26594
26595    fn generate_partition_by_list_property(&mut self, e: &PartitionByListProperty) -> Result<()> {
26596        // PARTITION BY LIST (partition_expressions) (create_expressions)
26597        self.write_keyword("PARTITION BY LIST");
26598        if let Some(partition_exprs) = &e.partition_expressions {
26599            self.write(" (");
26600            // Unwrap Tuple for partition columns (don't generate outer parens from Tuple)
26601            self.generate_doris_partition_expressions(partition_exprs)?;
26602            self.write(")");
26603        }
26604        if let Some(create_exprs) = &e.create_expressions {
26605            self.write(" (");
26606            // Unwrap Tuple for partition definitions
26607            self.generate_doris_partition_definitions(create_exprs)?;
26608            self.write(")");
26609        }
26610        Ok(())
26611    }
26612
26613    fn generate_partition_by_range_property(&mut self, e: &PartitionByRangeProperty) -> Result<()> {
26614        // PARTITION BY RANGE (partition_expressions) (create_expressions)
26615        self.write_keyword("PARTITION BY RANGE");
26616        if let Some(partition_exprs) = &e.partition_expressions {
26617            self.write(" (");
26618            // Unwrap Tuple for partition columns (don't generate outer parens from Tuple)
26619            self.generate_doris_partition_expressions(partition_exprs)?;
26620            self.write(")");
26621        }
26622        if let Some(create_exprs) = &e.create_expressions {
26623            self.write(" (");
26624            // Check for dynamic partition (PartitionByRangePropertyDynamic) or static (Tuple of Partition)
26625            self.generate_doris_partition_definitions(create_exprs)?;
26626            self.write(")");
26627        }
26628        Ok(())
26629    }
26630
26631    /// Generate Doris partition column expressions (unwrap Tuple)
26632    fn generate_doris_partition_expressions(&mut self, expr: &Expression) -> Result<()> {
26633        if let Expression::Tuple(t) = expr {
26634            for (i, e) in t.expressions.iter().enumerate() {
26635                if i > 0 {
26636                    self.write(", ");
26637                }
26638                self.generate_expression(e)?;
26639            }
26640        } else {
26641            self.generate_expression(expr)?;
26642        }
26643        Ok(())
26644    }
26645
26646    /// Generate Doris partition definitions (comma-separated Partition expressions)
26647    fn generate_doris_partition_definitions(&mut self, expr: &Expression) -> Result<()> {
26648        match expr {
26649            Expression::Tuple(t) => {
26650                // Multiple partitions, comma-separated
26651                for (i, part) in t.expressions.iter().enumerate() {
26652                    if i > 0 {
26653                        self.write(", ");
26654                    }
26655                    // For Partition expressions, generate the inner PartitionRange/PartitionList directly
26656                    if let Expression::Partition(p) = part {
26657                        for (j, inner) in p.expressions.iter().enumerate() {
26658                            if j > 0 {
26659                                self.write(", ");
26660                            }
26661                            self.generate_expression(inner)?;
26662                        }
26663                    } else {
26664                        self.generate_expression(part)?;
26665                    }
26666                }
26667            }
26668            Expression::PartitionByRangePropertyDynamic(_) => {
26669                // Dynamic partition - FROM/TO/INTERVAL
26670                self.generate_expression(expr)?;
26671            }
26672            _ => {
26673                self.generate_expression(expr)?;
26674            }
26675        }
26676        Ok(())
26677    }
26678
26679    fn generate_partition_by_range_property_dynamic(&mut self, e: &PartitionByRangePropertyDynamic) -> Result<()> {
26680        // Doris: FROM (start) TO (end) INTERVAL n UNIT
26681        if let Some(start) = &e.start {
26682            self.write_keyword("FROM");
26683            self.write(" (");
26684            self.generate_expression(start)?;
26685            self.write(")");
26686        }
26687        if let Some(end) = &e.end {
26688            self.write_space();
26689            self.write_keyword("TO");
26690            self.write(" (");
26691            self.generate_expression(end)?;
26692            self.write(")");
26693        }
26694        if let Some(every) = &e.every {
26695            self.write_space();
26696            // Generate INTERVAL n UNIT (not quoted, for Doris dynamic partition)
26697            self.generate_doris_interval(every)?;
26698        }
26699        Ok(())
26700    }
26701
26702    /// Generate Doris-style interval without quoting numbers: INTERVAL n UNIT
26703    fn generate_doris_interval(&mut self, expr: &Expression) -> Result<()> {
26704        if let Expression::Interval(interval) = expr {
26705            self.write_keyword("INTERVAL");
26706            if let Some(ref value) = interval.this {
26707                self.write_space();
26708                // Don't quote numbers for Doris partition interval
26709                self.generate_expression(value)?;
26710            }
26711            if let Some(ref unit_spec) = interval.unit {
26712                self.write_space();
26713                self.write_interval_unit_spec(unit_spec)?;
26714            }
26715            Ok(())
26716        } else {
26717            self.generate_expression(expr)
26718        }
26719    }
26720
26721    fn generate_partition_by_truncate(&mut self, e: &PartitionByTruncate) -> Result<()> {
26722        // TRUNCATE(expression, this)
26723        self.write_keyword("TRUNCATE");
26724        self.write("(");
26725        self.generate_expression(&e.expression)?;
26726        self.write(", ");
26727        self.generate_expression(&e.this)?;
26728        self.write(")");
26729        Ok(())
26730    }
26731
26732    fn generate_partition_list(&mut self, e: &PartitionList) -> Result<()> {
26733        // Doris: PARTITION name VALUES IN (val1, val2)
26734        self.write_keyword("PARTITION");
26735        self.write_space();
26736        self.generate_expression(&e.this)?;
26737        self.write_space();
26738        self.write_keyword("VALUES IN");
26739        self.write(" (");
26740        for (i, expr) in e.expressions.iter().enumerate() {
26741            if i > 0 {
26742                self.write(", ");
26743            }
26744            self.generate_expression(expr)?;
26745        }
26746        self.write(")");
26747        Ok(())
26748    }
26749
26750    fn generate_partition_range(&mut self, e: &PartitionRange) -> Result<()> {
26751        // Check if this is a TSQL-style simple range (e.g., "2 TO 5")
26752        // TSQL ranges have no expressions and just use `this TO expression`
26753        if e.expressions.is_empty() && e.expression.is_some() {
26754            // TSQL: simple range like "2 TO 5" - no PARTITION keyword
26755            self.generate_expression(&e.this)?;
26756            self.write_space();
26757            self.write_keyword("TO");
26758            self.write_space();
26759            self.generate_expression(e.expression.as_ref().unwrap())?;
26760            return Ok(());
26761        }
26762
26763        // Doris: PARTITION name VALUES LESS THAN (val) or PARTITION name VALUES [(val1), (val2))
26764        self.write_keyword("PARTITION");
26765        self.write_space();
26766        self.generate_expression(&e.this)?;
26767        self.write_space();
26768
26769        // Check if expressions contain Tuple (bracket notation) or single values (LESS THAN)
26770        if e.expressions.len() == 1 {
26771            // Single value: VALUES LESS THAN (val)
26772            self.write_keyword("VALUES LESS THAN");
26773            self.write(" (");
26774            self.generate_expression(&e.expressions[0])?;
26775            self.write(")");
26776        } else if !e.expressions.is_empty() {
26777            // Multiple values with Tuple: VALUES [(val1), (val2))
26778            self.write_keyword("VALUES");
26779            self.write(" [");
26780            for (i, expr) in e.expressions.iter().enumerate() {
26781                if i > 0 {
26782                    self.write(", ");
26783                }
26784                // If the expr is a Tuple, generate its contents wrapped in parens
26785                if let Expression::Tuple(t) = expr {
26786                    self.write("(");
26787                    for (j, inner) in t.expressions.iter().enumerate() {
26788                        if j > 0 {
26789                            self.write(", ");
26790                        }
26791                        self.generate_expression(inner)?;
26792                    }
26793                    self.write(")");
26794                } else {
26795                    self.write("(");
26796                    self.generate_expression(expr)?;
26797                    self.write(")");
26798                }
26799            }
26800            self.write(")");
26801        }
26802        Ok(())
26803    }
26804
26805    fn generate_partitioned_by_bucket(&mut self, e: &PartitionedByBucket) -> Result<()> {
26806        // BUCKET(this, expression)
26807        self.write_keyword("BUCKET");
26808        self.write("(");
26809        self.generate_expression(&e.this)?;
26810        self.write(", ");
26811        self.generate_expression(&e.expression)?;
26812        self.write(")");
26813        Ok(())
26814    }
26815
26816    fn generate_partitioned_by_property(&mut self, e: &PartitionedByProperty) -> Result<()> {
26817        // PARTITIONED BY this (Teradata/ClickHouse use PARTITION BY)
26818        if matches!(
26819            self.config.dialect,
26820            Some(crate::dialects::DialectType::Teradata) | Some(crate::dialects::DialectType::ClickHouse)
26821        ) {
26822            self.write_keyword("PARTITION BY");
26823        } else {
26824            self.write_keyword("PARTITIONED BY");
26825        }
26826        self.write_space();
26827        // In pretty mode, always use multiline tuple format for PARTITIONED BY
26828        if self.config.pretty {
26829            if let Expression::Tuple(ref tuple) = *e.this {
26830                self.write("(");
26831                self.write_newline();
26832                self.indent_level += 1;
26833                for (i, expr) in tuple.expressions.iter().enumerate() {
26834                    if i > 0 {
26835                        self.write(",");
26836                        self.write_newline();
26837                    }
26838                    self.write_indent();
26839                    self.generate_expression(expr)?;
26840                }
26841                self.indent_level -= 1;
26842                self.write_newline();
26843                self.write(")");
26844            } else {
26845                self.generate_expression(&e.this)?;
26846            }
26847        } else {
26848            self.generate_expression(&e.this)?;
26849        }
26850        Ok(())
26851    }
26852
26853    fn generate_partitioned_of_property(&mut self, e: &PartitionedOfProperty) -> Result<()> {
26854        // PARTITION OF this FOR VALUES expression or PARTITION OF this DEFAULT
26855        self.write_keyword("PARTITION OF");
26856        self.write_space();
26857        self.generate_expression(&e.this)?;
26858        // Check if expression is a PartitionBoundSpec
26859        if let Expression::PartitionBoundSpec(_) = e.expression.as_ref() {
26860            self.write_space();
26861            self.write_keyword("FOR VALUES");
26862            self.write_space();
26863            self.generate_expression(&e.expression)?;
26864        } else {
26865            self.write_space();
26866            self.write_keyword("DEFAULT");
26867        }
26868        Ok(())
26869    }
26870
26871    fn generate_period_for_system_time_constraint(&mut self, e: &PeriodForSystemTimeConstraint) -> Result<()> {
26872        // PERIOD FOR SYSTEM_TIME (this, expression)
26873        self.write_keyword("PERIOD FOR SYSTEM_TIME");
26874        self.write(" (");
26875        self.generate_expression(&e.this)?;
26876        self.write(", ");
26877        self.generate_expression(&e.expression)?;
26878        self.write(")");
26879        Ok(())
26880    }
26881
26882    fn generate_pivot_alias(&mut self, e: &PivotAlias) -> Result<()> {
26883        // value AS alias
26884        // The alias can be an identifier or an expression (e.g., string concatenation)
26885        self.generate_expression(&e.this)?;
26886        self.write_space();
26887        self.write_keyword("AS");
26888        self.write_space();
26889        // When target dialect uses identifiers for UNPIVOT aliases, convert literals to identifiers
26890        if self.config.unpivot_aliases_are_identifiers {
26891            match &e.alias {
26892                Expression::Literal(Literal::String(s)) => {
26893                    // Convert string literal to identifier
26894                    self.generate_identifier(&Identifier::new(s.clone()))?;
26895                }
26896                Expression::Literal(Literal::Number(n)) => {
26897                    // Convert number literal to quoted identifier
26898                    let mut id = Identifier::new(n.clone());
26899                    id.quoted = true;
26900                    self.generate_identifier(&id)?;
26901                }
26902                other => {
26903                    self.generate_expression(other)?;
26904                }
26905            }
26906        } else {
26907            self.generate_expression(&e.alias)?;
26908        }
26909        Ok(())
26910    }
26911
26912    fn generate_pivot_any(&mut self, e: &PivotAny) -> Result<()> {
26913        // ANY or ANY [expression]
26914        self.write_keyword("ANY");
26915        if let Some(this) = &e.this {
26916            self.write_space();
26917            self.generate_expression(this)?;
26918        }
26919        Ok(())
26920    }
26921
26922    fn generate_predict(&mut self, e: &Predict) -> Result<()> {
26923        // ML.PREDICT(MODEL this, expression, [params_struct])
26924        self.write_keyword("ML.PREDICT");
26925        self.write("(");
26926        self.write_keyword("MODEL");
26927        self.write_space();
26928        self.generate_expression(&e.this)?;
26929        self.write(", ");
26930        self.generate_expression(&e.expression)?;
26931        if let Some(params) = &e.params_struct {
26932            self.write(", ");
26933            self.generate_expression(params)?;
26934        }
26935        self.write(")");
26936        Ok(())
26937    }
26938
26939    fn generate_previous_day(&mut self, e: &PreviousDay) -> Result<()> {
26940        // PREVIOUS_DAY(this, expression)
26941        self.write_keyword("PREVIOUS_DAY");
26942        self.write("(");
26943        self.generate_expression(&e.this)?;
26944        self.write(", ");
26945        self.generate_expression(&e.expression)?;
26946        self.write(")");
26947        Ok(())
26948    }
26949
26950    fn generate_primary_key(&mut self, e: &PrimaryKey) -> Result<()> {
26951        // PRIMARY KEY [name] (columns) [INCLUDE (...)] [options]
26952        self.write_keyword("PRIMARY KEY");
26953        if let Some(name) = &e.this {
26954            self.write_space();
26955            self.generate_expression(name)?;
26956        }
26957        if !e.expressions.is_empty() {
26958            self.write(" (");
26959            for (i, expr) in e.expressions.iter().enumerate() {
26960                if i > 0 {
26961                    self.write(", ");
26962                }
26963                self.generate_expression(expr)?;
26964            }
26965            self.write(")");
26966        }
26967        if let Some(include) = &e.include {
26968            self.write_space();
26969            self.generate_expression(include)?;
26970        }
26971        if !e.options.is_empty() {
26972            self.write_space();
26973            for (i, opt) in e.options.iter().enumerate() {
26974                if i > 0 {
26975                    self.write_space();
26976                }
26977                self.generate_expression(opt)?;
26978            }
26979        }
26980        Ok(())
26981    }
26982
26983    fn generate_primary_key_column_constraint(&mut self, _e: &PrimaryKeyColumnConstraint) -> Result<()> {
26984        // PRIMARY KEY constraint at column level
26985        self.write_keyword("PRIMARY KEY");
26986        Ok(())
26987    }
26988
26989    fn generate_path_column_constraint(&mut self, e: &PathColumnConstraint) -> Result<()> {
26990        // PATH 'xpath' constraint for XMLTABLE/JSON_TABLE columns
26991        self.write_keyword("PATH");
26992        self.write_space();
26993        self.generate_expression(&e.this)?;
26994        Ok(())
26995    }
26996
26997    fn generate_projection_def(&mut self, e: &ProjectionDef) -> Result<()> {
26998        // PROJECTION this (expression)
26999        self.write_keyword("PROJECTION");
27000        self.write_space();
27001        self.generate_expression(&e.this)?;
27002        self.write(" (");
27003        self.generate_expression(&e.expression)?;
27004        self.write(")");
27005        Ok(())
27006    }
27007
27008    fn generate_properties(&mut self, e: &Properties) -> Result<()> {
27009        // Properties list
27010        for (i, prop) in e.expressions.iter().enumerate() {
27011            if i > 0 {
27012                self.write(", ");
27013            }
27014            self.generate_expression(prop)?;
27015        }
27016        Ok(())
27017    }
27018
27019    fn generate_property(&mut self, e: &Property) -> Result<()> {
27020        // name=value
27021        self.generate_expression(&e.this)?;
27022        if let Some(value) = &e.value {
27023            self.write("=");
27024            self.generate_expression(value)?;
27025        }
27026        Ok(())
27027    }
27028
27029    /// Generate BigQuery-style OPTIONS clause: OPTIONS (key=value, key=value, ...)
27030    fn generate_options_clause(&mut self, options: &[Expression]) -> Result<()> {
27031        self.write_keyword("OPTIONS");
27032        self.write(" (");
27033        for (i, opt) in options.iter().enumerate() {
27034            if i > 0 {
27035                self.write(", ");
27036            }
27037            self.generate_option_expression(opt)?;
27038        }
27039        self.write(")");
27040        Ok(())
27041    }
27042
27043    /// Generate Doris/StarRocks-style PROPERTIES clause: PROPERTIES ('key'='value', 'key'='value', ...)
27044    fn generate_properties_clause(&mut self, properties: &[Expression]) -> Result<()> {
27045        self.write_keyword("PROPERTIES");
27046        self.write(" (");
27047        for (i, prop) in properties.iter().enumerate() {
27048            if i > 0 {
27049                self.write(", ");
27050            }
27051            self.generate_option_expression(prop)?;
27052        }
27053        self.write(")");
27054        Ok(())
27055    }
27056
27057    /// Generate Databricks-style ENVIRONMENT clause: ENVIRONMENT (key = 'value', key = 'value', ...)
27058    fn generate_environment_clause(&mut self, environment: &[Expression]) -> Result<()> {
27059        self.write_keyword("ENVIRONMENT");
27060        self.write(" (");
27061        for (i, env_item) in environment.iter().enumerate() {
27062            if i > 0 {
27063                self.write(", ");
27064            }
27065            self.generate_environment_expression(env_item)?;
27066        }
27067        self.write(")");
27068        Ok(())
27069    }
27070
27071    /// Generate an environment expression with spaces around =
27072    fn generate_environment_expression(&mut self, expr: &Expression) -> Result<()> {
27073        match expr {
27074            Expression::Eq(eq) => {
27075                // Generate key = value with spaces (Databricks ENVIRONMENT style)
27076                self.generate_expression(&eq.left)?;
27077                self.write(" = ");
27078                self.generate_expression(&eq.right)?;
27079                Ok(())
27080            }
27081            _ => self.generate_expression(expr),
27082        }
27083    }
27084
27085    /// Generate Hive-style TBLPROPERTIES clause: TBLPROPERTIES ('key'='value', ...)
27086    fn generate_tblproperties_clause(&mut self, options: &[Expression]) -> Result<()> {
27087        self.write_keyword("TBLPROPERTIES");
27088        if self.config.pretty {
27089            self.write(" (");
27090            self.write_newline();
27091            self.indent_level += 1;
27092            for (i, opt) in options.iter().enumerate() {
27093                if i > 0 {
27094                    self.write(",");
27095                    self.write_newline();
27096                }
27097                self.write_indent();
27098                self.generate_option_expression(opt)?;
27099            }
27100            self.indent_level -= 1;
27101            self.write_newline();
27102            self.write(")");
27103        } else {
27104            self.write(" (");
27105            for (i, opt) in options.iter().enumerate() {
27106                if i > 0 {
27107                    self.write(", ");
27108                }
27109                self.generate_option_expression(opt)?;
27110            }
27111            self.write(")");
27112        }
27113        Ok(())
27114    }
27115
27116    /// Generate an option expression without spaces around =
27117    fn generate_option_expression(&mut self, expr: &Expression) -> Result<()> {
27118        match expr {
27119            Expression::Eq(eq) => {
27120                // Generate key=value without spaces
27121                self.generate_expression(&eq.left)?;
27122                self.write("=");
27123                self.generate_expression(&eq.right)?;
27124                Ok(())
27125            }
27126            _ => self.generate_expression(expr),
27127        }
27128    }
27129
27130    fn generate_pseudo_type(&mut self, e: &PseudoType) -> Result<()> {
27131        // Just output the name
27132        self.generate_expression(&e.this)?;
27133        Ok(())
27134    }
27135
27136    fn generate_put(&mut self, e: &PutStmt) -> Result<()> {
27137        // PUT source_file @stage [options]
27138        self.write_keyword("PUT");
27139        self.write_space();
27140
27141        // Source file path - preserve original quoting
27142        if e.source_quoted {
27143            self.write("'");
27144            self.write(&e.source);
27145            self.write("'");
27146        } else {
27147            self.write(&e.source);
27148        }
27149
27150        self.write_space();
27151
27152        // Target stage reference - output the string directly (includes @)
27153        if let Expression::Literal(Literal::String(s)) = &e.target {
27154            self.write(s);
27155        } else {
27156            self.generate_expression(&e.target)?;
27157        }
27158
27159        // Optional parameters: KEY=VALUE
27160        for param in &e.params {
27161            self.write_space();
27162            self.write(&param.name);
27163            if let Some(ref value) = param.value {
27164                self.write("=");
27165                self.generate_expression(value)?;
27166            }
27167        }
27168
27169        Ok(())
27170    }
27171
27172    fn generate_quantile(&mut self, e: &Quantile) -> Result<()> {
27173        // QUANTILE(this, quantile)
27174        self.write_keyword("QUANTILE");
27175        self.write("(");
27176        self.generate_expression(&e.this)?;
27177        if let Some(quantile) = &e.quantile {
27178            self.write(", ");
27179            self.generate_expression(quantile)?;
27180        }
27181        self.write(")");
27182        Ok(())
27183    }
27184
27185    fn generate_query_band(&mut self, e: &QueryBand) -> Result<()> {
27186        // QUERY_BAND = this [UPDATE] [FOR scope]
27187        if matches!(self.config.dialect, Some(crate::dialects::DialectType::Teradata)) {
27188            self.write_keyword("SET");
27189            self.write_space();
27190        }
27191        self.write_keyword("QUERY_BAND");
27192        self.write(" = ");
27193        self.generate_expression(&e.this)?;
27194        if e.update.is_some() {
27195            self.write_space();
27196            self.write_keyword("UPDATE");
27197        }
27198        if let Some(scope) = &e.scope {
27199            self.write_space();
27200            self.write_keyword("FOR");
27201            self.write_space();
27202            self.generate_expression(scope)?;
27203        }
27204        Ok(())
27205    }
27206
27207    fn generate_query_option(&mut self, e: &QueryOption) -> Result<()> {
27208        // this = expression
27209        self.generate_expression(&e.this)?;
27210        if let Some(expression) = &e.expression {
27211            self.write(" = ");
27212            self.generate_expression(expression)?;
27213        }
27214        Ok(())
27215    }
27216
27217    fn generate_query_transform(&mut self, e: &QueryTransform) -> Result<()> {
27218        // TRANSFORM (expressions) [row_format_before] [RECORDWRITER record_writer] USING command_script [AS schema] [row_format_after] [RECORDREADER record_reader]
27219        self.write_keyword("TRANSFORM");
27220        self.write("(");
27221        for (i, expr) in e.expressions.iter().enumerate() {
27222            if i > 0 {
27223                self.write(", ");
27224            }
27225            self.generate_expression(expr)?;
27226        }
27227        self.write(")");
27228        if let Some(row_format_before) = &e.row_format_before {
27229            self.write_space();
27230            self.generate_expression(row_format_before)?;
27231        }
27232        if let Some(record_writer) = &e.record_writer {
27233            self.write_space();
27234            self.write_keyword("RECORDWRITER");
27235            self.write_space();
27236            self.generate_expression(record_writer)?;
27237        }
27238        if let Some(command_script) = &e.command_script {
27239            self.write_space();
27240            self.write_keyword("USING");
27241            self.write_space();
27242            self.generate_expression(command_script)?;
27243        }
27244        if let Some(schema) = &e.schema {
27245            self.write_space();
27246            self.write_keyword("AS");
27247            self.write_space();
27248            self.generate_expression(schema)?;
27249        }
27250        if let Some(row_format_after) = &e.row_format_after {
27251            self.write_space();
27252            self.generate_expression(row_format_after)?;
27253        }
27254        if let Some(record_reader) = &e.record_reader {
27255            self.write_space();
27256            self.write_keyword("RECORDREADER");
27257            self.write_space();
27258            self.generate_expression(record_reader)?;
27259        }
27260        Ok(())
27261    }
27262
27263    fn generate_randn(&mut self, e: &Randn) -> Result<()> {
27264        // RANDN([seed])
27265        self.write_keyword("RANDN");
27266        self.write("(");
27267        if let Some(this) = &e.this {
27268            self.generate_expression(this)?;
27269        }
27270        self.write(")");
27271        Ok(())
27272    }
27273
27274    fn generate_randstr(&mut self, e: &Randstr) -> Result<()> {
27275        // RANDSTR(this, [generator])
27276        self.write_keyword("RANDSTR");
27277        self.write("(");
27278        self.generate_expression(&e.this)?;
27279        if let Some(generator) = &e.generator {
27280            self.write(", ");
27281            self.generate_expression(generator)?;
27282        }
27283        self.write(")");
27284        Ok(())
27285    }
27286
27287    fn generate_range_bucket(&mut self, e: &RangeBucket) -> Result<()> {
27288        // RANGE_BUCKET(this, expression)
27289        self.write_keyword("RANGE_BUCKET");
27290        self.write("(");
27291        self.generate_expression(&e.this)?;
27292        self.write(", ");
27293        self.generate_expression(&e.expression)?;
27294        self.write(")");
27295        Ok(())
27296    }
27297
27298    fn generate_range_n(&mut self, e: &RangeN) -> Result<()> {
27299        // RANGE_N(this BETWEEN expressions [EACH each])
27300        self.write_keyword("RANGE_N");
27301        self.write("(");
27302        self.generate_expression(&e.this)?;
27303        self.write_space();
27304        self.write_keyword("BETWEEN");
27305        self.write_space();
27306        for (i, expr) in e.expressions.iter().enumerate() {
27307            if i > 0 {
27308                self.write(", ");
27309            }
27310            self.generate_expression(expr)?;
27311        }
27312        if let Some(each) = &e.each {
27313            self.write_space();
27314            self.write_keyword("EACH");
27315            self.write_space();
27316            self.generate_expression(each)?;
27317        }
27318        self.write(")");
27319        Ok(())
27320    }
27321
27322    fn generate_read_csv(&mut self, e: &ReadCSV) -> Result<()> {
27323        // READ_CSV(this, expressions...)
27324        self.write_keyword("READ_CSV");
27325        self.write("(");
27326        self.generate_expression(&e.this)?;
27327        for expr in &e.expressions {
27328            self.write(", ");
27329            self.generate_expression(expr)?;
27330        }
27331        self.write(")");
27332        Ok(())
27333    }
27334
27335    fn generate_read_parquet(&mut self, e: &ReadParquet) -> Result<()> {
27336        // READ_PARQUET(expressions...)
27337        self.write_keyword("READ_PARQUET");
27338        self.write("(");
27339        for (i, expr) in e.expressions.iter().enumerate() {
27340            if i > 0 {
27341                self.write(", ");
27342            }
27343            self.generate_expression(expr)?;
27344        }
27345        self.write(")");
27346        Ok(())
27347    }
27348
27349    fn generate_recursive_with_search(&mut self, e: &RecursiveWithSearch) -> Result<()> {
27350        // SEARCH kind FIRST BY this SET expression [USING using]
27351        // or CYCLE this SET expression [USING using]
27352        if e.kind == "CYCLE" {
27353            self.write_keyword("CYCLE");
27354        } else {
27355            self.write_keyword("SEARCH");
27356            self.write_space();
27357            self.write(&e.kind);
27358            self.write_space();
27359            self.write_keyword("FIRST BY");
27360        }
27361        self.write_space();
27362        self.generate_expression(&e.this)?;
27363        self.write_space();
27364        self.write_keyword("SET");
27365        self.write_space();
27366        self.generate_expression(&e.expression)?;
27367        if let Some(using) = &e.using {
27368            self.write_space();
27369            self.write_keyword("USING");
27370            self.write_space();
27371            self.generate_expression(using)?;
27372        }
27373        Ok(())
27374    }
27375
27376    fn generate_reduce(&mut self, e: &Reduce) -> Result<()> {
27377        // REDUCE(this, initial, merge, [finish])
27378        self.write_keyword("REDUCE");
27379        self.write("(");
27380        self.generate_expression(&e.this)?;
27381        if let Some(initial) = &e.initial {
27382            self.write(", ");
27383            self.generate_expression(initial)?;
27384        }
27385        if let Some(merge) = &e.merge {
27386            self.write(", ");
27387            self.generate_expression(merge)?;
27388        }
27389        if let Some(finish) = &e.finish {
27390            self.write(", ");
27391            self.generate_expression(finish)?;
27392        }
27393        self.write(")");
27394        Ok(())
27395    }
27396
27397    fn generate_reference(&mut self, e: &Reference) -> Result<()> {
27398        // REFERENCES this (expressions) [options]
27399        self.write_keyword("REFERENCES");
27400        self.write_space();
27401        self.generate_expression(&e.this)?;
27402        if !e.expressions.is_empty() {
27403            self.write(" (");
27404            for (i, expr) in e.expressions.iter().enumerate() {
27405                if i > 0 {
27406                    self.write(", ");
27407                }
27408                self.generate_expression(expr)?;
27409            }
27410            self.write(")");
27411        }
27412        for opt in &e.options {
27413            self.write_space();
27414            self.generate_expression(opt)?;
27415        }
27416        Ok(())
27417    }
27418
27419    fn generate_refresh(&mut self, e: &Refresh) -> Result<()> {
27420        // REFRESH [kind] this
27421        self.write_keyword("REFRESH");
27422        if !e.kind.is_empty() {
27423            self.write_space();
27424            self.write_keyword(&e.kind);
27425        }
27426        self.write_space();
27427        self.generate_expression(&e.this)?;
27428        Ok(())
27429    }
27430
27431    fn generate_refresh_trigger_property(&mut self, e: &RefreshTriggerProperty) -> Result<()> {
27432        // Doris REFRESH clause: REFRESH method ON kind [EVERY n UNIT] [STARTS 'datetime']
27433        self.write_keyword("REFRESH");
27434        self.write_space();
27435        self.write_keyword(&e.method);
27436
27437        if let Some(ref kind) = e.kind {
27438            self.write_space();
27439            self.write_keyword("ON");
27440            self.write_space();
27441            self.write_keyword(kind);
27442
27443            // EVERY n UNIT
27444            if let Some(ref every) = e.every {
27445                self.write_space();
27446                self.write_keyword("EVERY");
27447                self.write_space();
27448                self.generate_expression(every)?;
27449                if let Some(ref unit) = e.unit {
27450                    self.write_space();
27451                    self.write_keyword(unit);
27452                }
27453            }
27454
27455            // STARTS 'datetime'
27456            if let Some(ref starts) = e.starts {
27457                self.write_space();
27458                self.write_keyword("STARTS");
27459                self.write_space();
27460                self.generate_expression(starts)?;
27461            }
27462        }
27463        Ok(())
27464    }
27465
27466    fn generate_regexp_count(&mut self, e: &RegexpCount) -> Result<()> {
27467        // REGEXP_COUNT(this, expression, position, parameters)
27468        self.write_keyword("REGEXP_COUNT");
27469        self.write("(");
27470        self.generate_expression(&e.this)?;
27471        self.write(", ");
27472        self.generate_expression(&e.expression)?;
27473        if let Some(position) = &e.position {
27474            self.write(", ");
27475            self.generate_expression(position)?;
27476        }
27477        if let Some(parameters) = &e.parameters {
27478            self.write(", ");
27479            self.generate_expression(parameters)?;
27480        }
27481        self.write(")");
27482        Ok(())
27483    }
27484
27485    fn generate_regexp_extract_all(&mut self, e: &RegexpExtractAll) -> Result<()> {
27486        // REGEXP_EXTRACT_ALL(this, expression, group, parameters, position, occurrence)
27487        self.write_keyword("REGEXP_EXTRACT_ALL");
27488        self.write("(");
27489        self.generate_expression(&e.this)?;
27490        self.write(", ");
27491        self.generate_expression(&e.expression)?;
27492        if let Some(group) = &e.group {
27493            self.write(", ");
27494            self.generate_expression(group)?;
27495        }
27496        self.write(")");
27497        Ok(())
27498    }
27499
27500    fn generate_regexp_full_match(&mut self, e: &RegexpFullMatch) -> Result<()> {
27501        // REGEXP_FULL_MATCH(this, expression)
27502        self.write_keyword("REGEXP_FULL_MATCH");
27503        self.write("(");
27504        self.generate_expression(&e.this)?;
27505        self.write(", ");
27506        self.generate_expression(&e.expression)?;
27507        self.write(")");
27508        Ok(())
27509    }
27510
27511    fn generate_regexp_i_like(&mut self, e: &RegexpILike) -> Result<()> {
27512        use crate::dialects::DialectType;
27513        // PostgreSQL/Redshift uses ~* operator for case-insensitive regex matching
27514        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) && e.flag.is_none() {
27515            self.generate_expression(&e.this)?;
27516            self.write(" ~* ");
27517            self.generate_expression(&e.expression)?;
27518        } else if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
27519            // Snowflake uses REGEXP_LIKE(x, pattern, 'i')
27520            self.write_keyword("REGEXP_LIKE");
27521            self.write("(");
27522            self.generate_expression(&e.this)?;
27523            self.write(", ");
27524            self.generate_expression(&e.expression)?;
27525            self.write(", ");
27526            if let Some(flag) = &e.flag {
27527                self.generate_expression(flag)?;
27528            } else {
27529                self.write("'i'");
27530            }
27531            self.write(")");
27532        } else {
27533            // this REGEXP_ILIKE expression or REGEXP_ILIKE(this, expression, flag)
27534            self.generate_expression(&e.this)?;
27535            self.write_space();
27536            self.write_keyword("REGEXP_ILIKE");
27537            self.write_space();
27538            self.generate_expression(&e.expression)?;
27539            if let Some(flag) = &e.flag {
27540                self.write(", ");
27541                self.generate_expression(flag)?;
27542            }
27543        }
27544        Ok(())
27545    }
27546
27547    fn generate_regexp_instr(&mut self, e: &RegexpInstr) -> Result<()> {
27548        // REGEXP_INSTR(this, expression, position, occurrence, option, parameters, group)
27549        self.write_keyword("REGEXP_INSTR");
27550        self.write("(");
27551        self.generate_expression(&e.this)?;
27552        self.write(", ");
27553        self.generate_expression(&e.expression)?;
27554        if let Some(position) = &e.position {
27555            self.write(", ");
27556            self.generate_expression(position)?;
27557        }
27558        if let Some(occurrence) = &e.occurrence {
27559            self.write(", ");
27560            self.generate_expression(occurrence)?;
27561        }
27562        if let Some(option) = &e.option {
27563            self.write(", ");
27564            self.generate_expression(option)?;
27565        }
27566        if let Some(parameters) = &e.parameters {
27567            self.write(", ");
27568            self.generate_expression(parameters)?;
27569        }
27570        if let Some(group) = &e.group {
27571            self.write(", ");
27572            self.generate_expression(group)?;
27573        }
27574        self.write(")");
27575        Ok(())
27576    }
27577
27578    fn generate_regexp_split(&mut self, e: &RegexpSplit) -> Result<()> {
27579        // REGEXP_SPLIT(this, expression, limit)
27580        self.write_keyword("REGEXP_SPLIT");
27581        self.write("(");
27582        self.generate_expression(&e.this)?;
27583        self.write(", ");
27584        self.generate_expression(&e.expression)?;
27585        if let Some(limit) = &e.limit {
27586            self.write(", ");
27587            self.generate_expression(limit)?;
27588        }
27589        self.write(")");
27590        Ok(())
27591    }
27592
27593    fn generate_regr_avgx(&mut self, e: &RegrAvgx) -> Result<()> {
27594        // REGR_AVGX(this, expression)
27595        self.write_keyword("REGR_AVGX");
27596        self.write("(");
27597        self.generate_expression(&e.this)?;
27598        self.write(", ");
27599        self.generate_expression(&e.expression)?;
27600        self.write(")");
27601        Ok(())
27602    }
27603
27604    fn generate_regr_avgy(&mut self, e: &RegrAvgy) -> Result<()> {
27605        // REGR_AVGY(this, expression)
27606        self.write_keyword("REGR_AVGY");
27607        self.write("(");
27608        self.generate_expression(&e.this)?;
27609        self.write(", ");
27610        self.generate_expression(&e.expression)?;
27611        self.write(")");
27612        Ok(())
27613    }
27614
27615    fn generate_regr_count(&mut self, e: &RegrCount) -> Result<()> {
27616        // REGR_COUNT(this, expression)
27617        self.write_keyword("REGR_COUNT");
27618        self.write("(");
27619        self.generate_expression(&e.this)?;
27620        self.write(", ");
27621        self.generate_expression(&e.expression)?;
27622        self.write(")");
27623        Ok(())
27624    }
27625
27626    fn generate_regr_intercept(&mut self, e: &RegrIntercept) -> Result<()> {
27627        // REGR_INTERCEPT(this, expression)
27628        self.write_keyword("REGR_INTERCEPT");
27629        self.write("(");
27630        self.generate_expression(&e.this)?;
27631        self.write(", ");
27632        self.generate_expression(&e.expression)?;
27633        self.write(")");
27634        Ok(())
27635    }
27636
27637    fn generate_regr_r2(&mut self, e: &RegrR2) -> Result<()> {
27638        // REGR_R2(this, expression)
27639        self.write_keyword("REGR_R2");
27640        self.write("(");
27641        self.generate_expression(&e.this)?;
27642        self.write(", ");
27643        self.generate_expression(&e.expression)?;
27644        self.write(")");
27645        Ok(())
27646    }
27647
27648    fn generate_regr_slope(&mut self, e: &RegrSlope) -> Result<()> {
27649        // REGR_SLOPE(this, expression)
27650        self.write_keyword("REGR_SLOPE");
27651        self.write("(");
27652        self.generate_expression(&e.this)?;
27653        self.write(", ");
27654        self.generate_expression(&e.expression)?;
27655        self.write(")");
27656        Ok(())
27657    }
27658
27659    fn generate_regr_sxx(&mut self, e: &RegrSxx) -> Result<()> {
27660        // REGR_SXX(this, expression)
27661        self.write_keyword("REGR_SXX");
27662        self.write("(");
27663        self.generate_expression(&e.this)?;
27664        self.write(", ");
27665        self.generate_expression(&e.expression)?;
27666        self.write(")");
27667        Ok(())
27668    }
27669
27670    fn generate_regr_sxy(&mut self, e: &RegrSxy) -> Result<()> {
27671        // REGR_SXY(this, expression)
27672        self.write_keyword("REGR_SXY");
27673        self.write("(");
27674        self.generate_expression(&e.this)?;
27675        self.write(", ");
27676        self.generate_expression(&e.expression)?;
27677        self.write(")");
27678        Ok(())
27679    }
27680
27681    fn generate_regr_syy(&mut self, e: &RegrSyy) -> Result<()> {
27682        // REGR_SYY(this, expression)
27683        self.write_keyword("REGR_SYY");
27684        self.write("(");
27685        self.generate_expression(&e.this)?;
27686        self.write(", ");
27687        self.generate_expression(&e.expression)?;
27688        self.write(")");
27689        Ok(())
27690    }
27691
27692    fn generate_regr_valx(&mut self, e: &RegrValx) -> Result<()> {
27693        // REGR_VALX(this, expression)
27694        self.write_keyword("REGR_VALX");
27695        self.write("(");
27696        self.generate_expression(&e.this)?;
27697        self.write(", ");
27698        self.generate_expression(&e.expression)?;
27699        self.write(")");
27700        Ok(())
27701    }
27702
27703    fn generate_regr_valy(&mut self, e: &RegrValy) -> Result<()> {
27704        // REGR_VALY(this, expression)
27705        self.write_keyword("REGR_VALY");
27706        self.write("(");
27707        self.generate_expression(&e.this)?;
27708        self.write(", ");
27709        self.generate_expression(&e.expression)?;
27710        self.write(")");
27711        Ok(())
27712    }
27713
27714    fn generate_remote_with_connection_model_property(&mut self, e: &RemoteWithConnectionModelProperty) -> Result<()> {
27715        // REMOTE WITH CONNECTION this
27716        self.write_keyword("REMOTE WITH CONNECTION");
27717        self.write_space();
27718        self.generate_expression(&e.this)?;
27719        Ok(())
27720    }
27721
27722    fn generate_rename_column(&mut self, e: &RenameColumn) -> Result<()> {
27723        // RENAME COLUMN [IF EXISTS] this TO new_name
27724        self.write_keyword("RENAME COLUMN");
27725        if e.exists {
27726            self.write_space();
27727            self.write_keyword("IF EXISTS");
27728        }
27729        self.write_space();
27730        self.generate_expression(&e.this)?;
27731        if let Some(to) = &e.to {
27732            self.write_space();
27733            self.write_keyword("TO");
27734            self.write_space();
27735            self.generate_expression(to)?;
27736        }
27737        Ok(())
27738    }
27739
27740    fn generate_replace_partition(&mut self, e: &ReplacePartition) -> Result<()> {
27741        // REPLACE PARTITION expression [FROM source]
27742        self.write_keyword("REPLACE PARTITION");
27743        self.write_space();
27744        self.generate_expression(&e.expression)?;
27745        if let Some(source) = &e.source {
27746            self.write_space();
27747            self.write_keyword("FROM");
27748            self.write_space();
27749            self.generate_expression(source)?;
27750        }
27751        Ok(())
27752    }
27753
27754    fn generate_returning(&mut self, e: &Returning) -> Result<()> {
27755        // RETURNING expressions [INTO into]
27756        // TSQL and Fabric use OUTPUT instead of RETURNING
27757        let keyword = match self.config.dialect {
27758            Some(DialectType::TSQL) | Some(DialectType::Fabric) => "OUTPUT",
27759            _ => "RETURNING",
27760        };
27761        self.write_keyword(keyword);
27762        self.write_space();
27763        for (i, expr) in e.expressions.iter().enumerate() {
27764            if i > 0 {
27765                self.write(", ");
27766            }
27767            self.generate_expression(expr)?;
27768        }
27769        if let Some(into) = &e.into {
27770            self.write_space();
27771            self.write_keyword("INTO");
27772            self.write_space();
27773            self.generate_expression(into)?;
27774        }
27775        Ok(())
27776    }
27777
27778    fn generate_output_clause(&mut self, output: &OutputClause) -> Result<()> {
27779        // OUTPUT expressions [INTO into_table]
27780        self.write_space();
27781        self.write_keyword("OUTPUT");
27782        self.write_space();
27783        for (i, expr) in output.columns.iter().enumerate() {
27784            if i > 0 {
27785                self.write(", ");
27786            }
27787            self.generate_expression(expr)?;
27788        }
27789        if let Some(into_table) = &output.into_table {
27790            self.write_space();
27791            self.write_keyword("INTO");
27792            self.write_space();
27793            self.generate_expression(into_table)?;
27794        }
27795        Ok(())
27796    }
27797
27798    fn generate_returns_property(&mut self, e: &ReturnsProperty) -> Result<()> {
27799        // RETURNS [TABLE] this [NULL ON NULL INPUT | CALLED ON NULL INPUT]
27800        self.write_keyword("RETURNS");
27801        if e.is_table.is_some() {
27802            self.write_space();
27803            self.write_keyword("TABLE");
27804        }
27805        if let Some(table) = &e.table {
27806            self.write_space();
27807            self.generate_expression(table)?;
27808        } else if let Some(this) = &e.this {
27809            self.write_space();
27810            self.generate_expression(this)?;
27811        }
27812        if e.null.is_some() {
27813            self.write_space();
27814            self.write_keyword("NULL ON NULL INPUT");
27815        }
27816        Ok(())
27817    }
27818
27819    fn generate_rollback(&mut self, e: &Rollback) -> Result<()> {
27820        // ROLLBACK [TRANSACTION [transaction_name]] [TO savepoint]
27821        self.write_keyword("ROLLBACK");
27822
27823        // TSQL always uses ROLLBACK TRANSACTION
27824        if e.this.is_none() && matches!(self.config.dialect, Some(DialectType::TSQL) | Some(DialectType::Fabric)) {
27825            self.write_space();
27826            self.write_keyword("TRANSACTION");
27827        }
27828
27829        // Check if this has TRANSACTION keyword or transaction name
27830        if let Some(this) = &e.this {
27831            // Check if it's just the "TRANSACTION" marker or an actual transaction name
27832            let is_transaction_marker = matches!(
27833                this.as_ref(),
27834                Expression::Identifier(id) if id.name == "TRANSACTION"
27835            );
27836
27837            self.write_space();
27838            self.write_keyword("TRANSACTION");
27839
27840            // If it's a real transaction name, output it
27841            if !is_transaction_marker {
27842                self.write_space();
27843                self.generate_expression(this)?;
27844            }
27845        }
27846
27847        // Output TO savepoint
27848        if let Some(savepoint) = &e.savepoint {
27849            self.write_space();
27850            self.write_keyword("TO");
27851            self.write_space();
27852            self.generate_expression(savepoint)?;
27853        }
27854        Ok(())
27855    }
27856
27857    fn generate_rollup(&mut self, e: &Rollup) -> Result<()> {
27858        // Python: return f"ROLLUP {self.wrap(expressions)}" if expressions else "WITH ROLLUP"
27859        if e.expressions.is_empty() {
27860            self.write_keyword("WITH ROLLUP");
27861        } else {
27862            self.write_keyword("ROLLUP");
27863            self.write("(");
27864            for (i, expr) in e.expressions.iter().enumerate() {
27865                if i > 0 {
27866                    self.write(", ");
27867                }
27868                self.generate_expression(expr)?;
27869            }
27870            self.write(")");
27871        }
27872        Ok(())
27873    }
27874
27875    fn generate_row_format_delimited_property(&mut self, e: &RowFormatDelimitedProperty) -> Result<()> {
27876        // ROW FORMAT DELIMITED [FIELDS TERMINATED BY ...] [ESCAPED BY ...] [COLLECTION ITEMS TERMINATED BY ...] [MAP KEYS TERMINATED BY ...] [LINES TERMINATED BY ...] [NULL DEFINED AS ...]
27877        self.write_keyword("ROW FORMAT DELIMITED");
27878        if let Some(fields) = &e.fields {
27879            self.write_space();
27880            self.write_keyword("FIELDS TERMINATED BY");
27881            self.write_space();
27882            self.generate_expression(fields)?;
27883        }
27884        if let Some(escaped) = &e.escaped {
27885            self.write_space();
27886            self.write_keyword("ESCAPED BY");
27887            self.write_space();
27888            self.generate_expression(escaped)?;
27889        }
27890        if let Some(items) = &e.collection_items {
27891            self.write_space();
27892            self.write_keyword("COLLECTION ITEMS TERMINATED BY");
27893            self.write_space();
27894            self.generate_expression(items)?;
27895        }
27896        if let Some(keys) = &e.map_keys {
27897            self.write_space();
27898            self.write_keyword("MAP KEYS TERMINATED BY");
27899            self.write_space();
27900            self.generate_expression(keys)?;
27901        }
27902        if let Some(lines) = &e.lines {
27903            self.write_space();
27904            self.write_keyword("LINES TERMINATED BY");
27905            self.write_space();
27906            self.generate_expression(lines)?;
27907        }
27908        if let Some(null) = &e.null {
27909            self.write_space();
27910            self.write_keyword("NULL DEFINED AS");
27911            self.write_space();
27912            self.generate_expression(null)?;
27913        }
27914        if let Some(serde) = &e.serde {
27915            self.write_space();
27916            self.generate_expression(serde)?;
27917        }
27918        Ok(())
27919    }
27920
27921    fn generate_row_format_property(&mut self, e: &RowFormatProperty) -> Result<()> {
27922        // ROW FORMAT this
27923        self.write_keyword("ROW FORMAT");
27924        self.write_space();
27925        self.generate_expression(&e.this)?;
27926        Ok(())
27927    }
27928
27929    fn generate_row_format_serde_property(&mut self, e: &RowFormatSerdeProperty) -> Result<()> {
27930        // ROW FORMAT SERDE this [WITH SERDEPROPERTIES (...)]
27931        self.write_keyword("ROW FORMAT SERDE");
27932        self.write_space();
27933        self.generate_expression(&e.this)?;
27934        if let Some(props) = &e.serde_properties {
27935            self.write_space();
27936            // SerdeProperties generates its own "[WITH] SERDEPROPERTIES (...)"
27937            self.generate_expression(props)?;
27938        }
27939        Ok(())
27940    }
27941
27942    fn generate_sha2(&mut self, e: &SHA2) -> Result<()> {
27943        // SHA2(this, length)
27944        self.write_keyword("SHA2");
27945        self.write("(");
27946        self.generate_expression(&e.this)?;
27947        if let Some(length) = e.length {
27948            self.write(", ");
27949            self.write(&length.to_string());
27950        }
27951        self.write(")");
27952        Ok(())
27953    }
27954
27955    fn generate_sha2_digest(&mut self, e: &SHA2Digest) -> Result<()> {
27956        // SHA2_DIGEST(this, length)
27957        self.write_keyword("SHA2_DIGEST");
27958        self.write("(");
27959        self.generate_expression(&e.this)?;
27960        if let Some(length) = e.length {
27961            self.write(", ");
27962            self.write(&length.to_string());
27963        }
27964        self.write(")");
27965        Ok(())
27966    }
27967
27968    fn generate_safe_add(&mut self, e: &SafeAdd) -> Result<()> {
27969        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
27970            "TRY_ADD"
27971        } else {
27972            "SAFE_ADD"
27973        };
27974        self.write_keyword(name);
27975        self.write("(");
27976        self.generate_expression(&e.this)?;
27977        self.write(", ");
27978        self.generate_expression(&e.expression)?;
27979        self.write(")");
27980        Ok(())
27981    }
27982
27983    fn generate_safe_divide(&mut self, e: &SafeDivide) -> Result<()> {
27984        // SAFE_DIVIDE(this, expression)
27985        self.write_keyword("SAFE_DIVIDE");
27986        self.write("(");
27987        self.generate_expression(&e.this)?;
27988        self.write(", ");
27989        self.generate_expression(&e.expression)?;
27990        self.write(")");
27991        Ok(())
27992    }
27993
27994    fn generate_safe_multiply(&mut self, e: &SafeMultiply) -> Result<()> {
27995        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
27996            "TRY_MULTIPLY"
27997        } else {
27998            "SAFE_MULTIPLY"
27999        };
28000        self.write_keyword(name);
28001        self.write("(");
28002        self.generate_expression(&e.this)?;
28003        self.write(", ");
28004        self.generate_expression(&e.expression)?;
28005        self.write(")");
28006        Ok(())
28007    }
28008
28009    fn generate_safe_subtract(&mut self, e: &SafeSubtract) -> Result<()> {
28010        let name = if matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark) | Some(crate::dialects::DialectType::Databricks)) {
28011            "TRY_SUBTRACT"
28012        } else {
28013            "SAFE_SUBTRACT"
28014        };
28015        self.write_keyword(name);
28016        self.write("(");
28017        self.generate_expression(&e.this)?;
28018        self.write(", ");
28019        self.generate_expression(&e.expression)?;
28020        self.write(")");
28021        Ok(())
28022    }
28023
28024    /// Generate the body of a USING SAMPLE or TABLESAMPLE clause:
28025    /// METHOD (size UNIT) [REPEATABLE (seed)]
28026    fn generate_sample_body(&mut self, sample: &Sample) -> Result<()> {
28027        // Handle BUCKET sampling: TABLESAMPLE (BUCKET n OUT OF m [ON col])
28028        if matches!(sample.method, SampleMethod::Bucket) {
28029            self.write(" (");
28030            self.write_keyword("BUCKET");
28031            self.write_space();
28032            if let Some(ref num) = sample.bucket_numerator {
28033                self.generate_expression(num)?;
28034            }
28035            self.write_space();
28036            self.write_keyword("OUT OF");
28037            self.write_space();
28038            if let Some(ref denom) = sample.bucket_denominator {
28039                self.generate_expression(denom)?;
28040            }
28041            if let Some(ref field) = sample.bucket_field {
28042                self.write_space();
28043                self.write_keyword("ON");
28044                self.write_space();
28045                self.generate_expression(field)?;
28046            }
28047            self.write(")");
28048            return Ok(());
28049        }
28050
28051        // Output method name if explicitly specified, or for dialects that always require it
28052        let is_snowflake = matches!(self.config.dialect, Some(crate::dialects::DialectType::Snowflake));
28053        let is_postgres = matches!(self.config.dialect, Some(crate::dialects::DialectType::PostgreSQL) | Some(crate::dialects::DialectType::Redshift));
28054        // Databricks and Spark don't output method names
28055        let is_databricks = matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks));
28056        let is_spark = matches!(self.config.dialect, Some(crate::dialects::DialectType::Spark));
28057        let suppress_method = is_databricks || is_spark || sample.suppress_method_output;
28058        // PostgreSQL always outputs BERNOULLI for BERNOULLI samples
28059        let force_method = is_postgres && matches!(sample.method, SampleMethod::Bernoulli);
28060        if !suppress_method && (sample.explicit_method || is_snowflake || force_method) {
28061            self.write_space();
28062            if !sample.explicit_method && (is_snowflake || force_method) {
28063                // Snowflake/PostgreSQL defaults to BERNOULLI when no method is specified
28064                self.write_keyword("BERNOULLI");
28065            } else {
28066                match sample.method {
28067                    SampleMethod::Bernoulli => self.write_keyword("BERNOULLI"),
28068                    SampleMethod::System => self.write_keyword("SYSTEM"),
28069                    SampleMethod::Block => self.write_keyword("BLOCK"),
28070                    SampleMethod::Row => self.write_keyword("ROW"),
28071                    SampleMethod::Reservoir => self.write_keyword("RESERVOIR"),
28072                    SampleMethod::Percent => self.write_keyword("SYSTEM"),
28073                    SampleMethod::Bucket => {} // handled above
28074                }
28075            }
28076        }
28077
28078        // Output size, with or without parentheses depending on dialect
28079        let emit_size_no_parens = !self.config.tablesample_requires_parens;
28080        if emit_size_no_parens {
28081            self.write_space();
28082            match &sample.size {
28083                Expression::Tuple(tuple) => {
28084                    for (i, expr) in tuple.expressions.iter().enumerate() {
28085                        if i > 0 {
28086                            self.write(", ");
28087                        }
28088                        self.generate_expression(expr)?;
28089                    }
28090                }
28091                expr => self.generate_expression(expr)?,
28092            }
28093        } else {
28094            self.write(" (");
28095            self.generate_expression(&sample.size)?;
28096        }
28097
28098        // Determine unit
28099        let is_rows_method = matches!(sample.method, SampleMethod::Reservoir | SampleMethod::Row | SampleMethod::Bucket);
28100        let is_percent = matches!(sample.method, SampleMethod::Percent | SampleMethod::System | SampleMethod::Bernoulli | SampleMethod::Block);
28101
28102        // For Snowflake, PostgreSQL, and Presto/Trino, only output ROWS/PERCENT when the user explicitly wrote it (unit_after_size).
28103        // These dialects use bare numbers for percentage by default in TABLESAMPLE METHOD(size) syntax.
28104        // For Databricks and Spark, always output PERCENT for percentage samples.
28105        let is_presto = matches!(self.config.dialect, Some(crate::dialects::DialectType::Presto) | Some(crate::dialects::DialectType::Trino) | Some(crate::dialects::DialectType::Athena));
28106        let should_output_unit = if is_databricks || is_spark {
28107            // Always output PERCENT for percentage-based methods, or ROWS for row-based methods
28108            is_percent || is_rows_method || sample.unit_after_size
28109        } else if is_snowflake || is_postgres || is_presto {
28110            sample.unit_after_size
28111        } else {
28112            sample.unit_after_size || (sample.explicit_method && (is_rows_method || is_percent))
28113        };
28114
28115        if should_output_unit {
28116            self.write_space();
28117            if sample.is_percent {
28118                self.write_keyword("PERCENT");
28119            } else if is_rows_method && !sample.unit_after_size {
28120                self.write_keyword("ROWS");
28121            } else if sample.unit_after_size {
28122                match sample.method {
28123                    SampleMethod::Percent | SampleMethod::System | SampleMethod::Bernoulli | SampleMethod::Block => {
28124                        self.write_keyword("PERCENT");
28125                    }
28126                    SampleMethod::Row | SampleMethod::Reservoir => {
28127                        self.write_keyword("ROWS");
28128                    }
28129                    _ => self.write_keyword("ROWS"),
28130                }
28131            } else {
28132                self.write_keyword("PERCENT");
28133            }
28134        }
28135
28136        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28137            if let Some(ref offset) = sample.offset {
28138                self.write_space();
28139                self.write_keyword("OFFSET");
28140                self.write_space();
28141                self.generate_expression(offset)?;
28142            }
28143        }
28144        if !emit_size_no_parens {
28145            self.write(")");
28146        }
28147
28148        Ok(())
28149    }
28150
28151    fn generate_sample_property(&mut self, e: &SampleProperty) -> Result<()> {
28152        // SAMPLE this (ClickHouse uses SAMPLE BY)
28153        if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28154            self.write_keyword("SAMPLE BY");
28155        } else {
28156            self.write_keyword("SAMPLE");
28157        }
28158        self.write_space();
28159        self.generate_expression(&e.this)?;
28160        Ok(())
28161    }
28162
28163    fn generate_schema(&mut self, e: &Schema) -> Result<()> {
28164        // this (expressions...)
28165        if let Some(this) = &e.this {
28166            self.generate_expression(this)?;
28167        }
28168        if !e.expressions.is_empty() {
28169            // Add space before column list if there's a preceding expression
28170            if e.this.is_some() {
28171                self.write_space();
28172            }
28173            self.write("(");
28174            for (i, expr) in e.expressions.iter().enumerate() {
28175                if i > 0 {
28176                    self.write(", ");
28177                }
28178                self.generate_expression(expr)?;
28179            }
28180            self.write(")");
28181        }
28182        Ok(())
28183    }
28184
28185    fn generate_schema_comment_property(&mut self, e: &SchemaCommentProperty) -> Result<()> {
28186        // COMMENT this
28187        self.write_keyword("COMMENT");
28188        self.write_space();
28189        self.generate_expression(&e.this)?;
28190        Ok(())
28191    }
28192
28193    fn generate_scope_resolution(&mut self, e: &ScopeResolution) -> Result<()> {
28194        // [this::]expression
28195        if let Some(this) = &e.this {
28196            self.generate_expression(this)?;
28197            self.write("::");
28198        }
28199        self.generate_expression(&e.expression)?;
28200        Ok(())
28201    }
28202
28203    fn generate_search(&mut self, e: &Search) -> Result<()> {
28204        // SEARCH(this, expression, [json_scope], [analyzer], [analyzer_options], [search_mode])
28205        self.write_keyword("SEARCH");
28206        self.write("(");
28207        self.generate_expression(&e.this)?;
28208        self.write(", ");
28209        self.generate_expression(&e.expression)?;
28210        if let Some(json_scope) = &e.json_scope {
28211            self.write(", ");
28212            self.generate_expression(json_scope)?;
28213        }
28214        if let Some(analyzer) = &e.analyzer {
28215            self.write(", ");
28216            self.generate_expression(analyzer)?;
28217        }
28218        if let Some(analyzer_options) = &e.analyzer_options {
28219            self.write(", ");
28220            self.generate_expression(analyzer_options)?;
28221        }
28222        if let Some(search_mode) = &e.search_mode {
28223            self.write(", ");
28224            self.generate_expression(search_mode)?;
28225        }
28226        self.write(")");
28227        Ok(())
28228    }
28229
28230    fn generate_search_ip(&mut self, e: &SearchIp) -> Result<()> {
28231        // SEARCH_IP(this, expression)
28232        self.write_keyword("SEARCH_IP");
28233        self.write("(");
28234        self.generate_expression(&e.this)?;
28235        self.write(", ");
28236        self.generate_expression(&e.expression)?;
28237        self.write(")");
28238        Ok(())
28239    }
28240
28241    fn generate_security_property(&mut self, e: &SecurityProperty) -> Result<()> {
28242        // SECURITY this
28243        self.write_keyword("SECURITY");
28244        self.write_space();
28245        self.generate_expression(&e.this)?;
28246        Ok(())
28247    }
28248
28249    fn generate_semantic_view(&mut self, e: &SemanticView) -> Result<()> {
28250        // SEMANTIC_VIEW(this [METRICS ...] [DIMENSIONS ...] [FACTS ...] [WHERE ...])
28251        self.write("SEMANTIC_VIEW(");
28252
28253        if self.config.pretty {
28254            // Pretty print: each clause on its own line
28255            self.write_newline();
28256            self.indent_level += 1;
28257            self.write_indent();
28258            self.generate_expression(&e.this)?;
28259
28260            if let Some(metrics) = &e.metrics {
28261                self.write_newline();
28262                self.write_indent();
28263                self.write_keyword("METRICS");
28264                self.write_space();
28265                self.generate_semantic_view_tuple(metrics)?;
28266            }
28267            if let Some(dimensions) = &e.dimensions {
28268                self.write_newline();
28269                self.write_indent();
28270                self.write_keyword("DIMENSIONS");
28271                self.write_space();
28272                self.generate_semantic_view_tuple(dimensions)?;
28273            }
28274            if let Some(facts) = &e.facts {
28275                self.write_newline();
28276                self.write_indent();
28277                self.write_keyword("FACTS");
28278                self.write_space();
28279                self.generate_semantic_view_tuple(facts)?;
28280            }
28281            if let Some(where_) = &e.where_ {
28282                self.write_newline();
28283                self.write_indent();
28284                self.write_keyword("WHERE");
28285                self.write_space();
28286                self.generate_expression(where_)?;
28287            }
28288            self.write_newline();
28289            self.indent_level -= 1;
28290            self.write_indent();
28291        } else {
28292            // Compact: all on one line
28293            self.generate_expression(&e.this)?;
28294            if let Some(metrics) = &e.metrics {
28295                self.write_space();
28296                self.write_keyword("METRICS");
28297                self.write_space();
28298                self.generate_semantic_view_tuple(metrics)?;
28299            }
28300            if let Some(dimensions) = &e.dimensions {
28301                self.write_space();
28302                self.write_keyword("DIMENSIONS");
28303                self.write_space();
28304                self.generate_semantic_view_tuple(dimensions)?;
28305            }
28306            if let Some(facts) = &e.facts {
28307                self.write_space();
28308                self.write_keyword("FACTS");
28309                self.write_space();
28310                self.generate_semantic_view_tuple(facts)?;
28311            }
28312            if let Some(where_) = &e.where_ {
28313                self.write_space();
28314                self.write_keyword("WHERE");
28315                self.write_space();
28316                self.generate_expression(where_)?;
28317            }
28318        }
28319        self.write(")");
28320        Ok(())
28321    }
28322
28323    /// Helper for SEMANTIC_VIEW tuple contents (without parentheses)
28324    fn generate_semantic_view_tuple(&mut self, expr: &Expression) -> Result<()> {
28325        if let Expression::Tuple(t) = expr {
28326            for (i, e) in t.expressions.iter().enumerate() {
28327                if i > 0 {
28328                    self.write(", ");
28329                }
28330                self.generate_expression(e)?;
28331            }
28332        } else {
28333            self.generate_expression(expr)?;
28334        }
28335        Ok(())
28336    }
28337
28338    fn generate_sequence_properties(&mut self, e: &SequenceProperties) -> Result<()> {
28339        // [START WITH start] [INCREMENT BY increment] [MINVALUE minvalue] [MAXVALUE maxvalue] [CACHE cache] [OWNED BY owned]
28340        if let Some(start) = &e.start {
28341            self.write_keyword("START WITH");
28342            self.write_space();
28343            self.generate_expression(start)?;
28344        }
28345        if let Some(increment) = &e.increment {
28346            self.write_space();
28347            self.write_keyword("INCREMENT BY");
28348            self.write_space();
28349            self.generate_expression(increment)?;
28350        }
28351        if let Some(minvalue) = &e.minvalue {
28352            self.write_space();
28353            self.write_keyword("MINVALUE");
28354            self.write_space();
28355            self.generate_expression(minvalue)?;
28356        }
28357        if let Some(maxvalue) = &e.maxvalue {
28358            self.write_space();
28359            self.write_keyword("MAXVALUE");
28360            self.write_space();
28361            self.generate_expression(maxvalue)?;
28362        }
28363        if let Some(cache) = &e.cache {
28364            self.write_space();
28365            self.write_keyword("CACHE");
28366            self.write_space();
28367            self.generate_expression(cache)?;
28368        }
28369        if let Some(owned) = &e.owned {
28370            self.write_space();
28371            self.write_keyword("OWNED BY");
28372            self.write_space();
28373            self.generate_expression(owned)?;
28374        }
28375        for opt in &e.options {
28376            self.write_space();
28377            self.generate_expression(opt)?;
28378        }
28379        Ok(())
28380    }
28381
28382    fn generate_serde_properties(&mut self, e: &SerdeProperties) -> Result<()> {
28383        // [WITH] SERDEPROPERTIES (expressions)
28384        if e.with_.is_some() {
28385            self.write_keyword("WITH");
28386            self.write_space();
28387        }
28388        self.write_keyword("SERDEPROPERTIES");
28389        self.write(" (");
28390        for (i, expr) in e.expressions.iter().enumerate() {
28391            if i > 0 {
28392                self.write(", ");
28393            }
28394            // Generate key=value without spaces around =
28395            match expr {
28396                Expression::Eq(eq) => {
28397                    self.generate_expression(&eq.left)?;
28398                    self.write("=");
28399                    self.generate_expression(&eq.right)?;
28400                }
28401                _ => self.generate_expression(expr)?,
28402            }
28403        }
28404        self.write(")");
28405        Ok(())
28406    }
28407
28408    fn generate_session_parameter(&mut self, e: &SessionParameter) -> Result<()> {
28409        // @@[kind.]this
28410        self.write("@@");
28411        if let Some(kind) = &e.kind {
28412            self.write(kind);
28413            self.write(".");
28414        }
28415        self.generate_expression(&e.this)?;
28416        Ok(())
28417    }
28418
28419    fn generate_set(&mut self, e: &Set) -> Result<()> {
28420        // SET/UNSET [TAG] expressions
28421        if e.unset.is_some() {
28422            self.write_keyword("UNSET");
28423        } else {
28424            self.write_keyword("SET");
28425        }
28426        if e.tag.is_some() {
28427            self.write_space();
28428            self.write_keyword("TAG");
28429        }
28430        if !e.expressions.is_empty() {
28431            self.write_space();
28432            for (i, expr) in e.expressions.iter().enumerate() {
28433                if i > 0 {
28434                    self.write(", ");
28435                }
28436                self.generate_expression(expr)?;
28437            }
28438        }
28439        Ok(())
28440    }
28441
28442    fn generate_set_config_property(&mut self, e: &SetConfigProperty) -> Result<()> {
28443        // SET this or SETCONFIG this
28444        self.write_keyword("SET");
28445        self.write_space();
28446        self.generate_expression(&e.this)?;
28447        Ok(())
28448    }
28449
28450    fn generate_set_item(&mut self, e: &SetItem) -> Result<()> {
28451        // [kind] name = value
28452        if let Some(kind) = &e.kind {
28453            self.write_keyword(kind);
28454            self.write_space();
28455        }
28456        self.generate_expression(&e.name)?;
28457        self.write(" = ");
28458        self.generate_expression(&e.value)?;
28459        Ok(())
28460    }
28461
28462    fn generate_set_operation(&mut self, e: &SetOperation) -> Result<()> {
28463        // [WITH ...] this UNION|INTERSECT|EXCEPT [ALL|DISTINCT] [BY NAME] expression
28464        if let Some(with_) = &e.with_ {
28465            self.generate_expression(with_)?;
28466            self.write_space();
28467        }
28468        self.generate_expression(&e.this)?;
28469        self.write_space();
28470        // kind should be UNION, INTERSECT, EXCEPT, etc.
28471        if let Some(kind) = &e.kind {
28472            self.write_keyword(kind);
28473        }
28474        if e.distinct {
28475            self.write_space();
28476            self.write_keyword("DISTINCT");
28477        } else {
28478            self.write_space();
28479            self.write_keyword("ALL");
28480        }
28481        if e.by_name.is_some() {
28482            self.write_space();
28483            self.write_keyword("BY NAME");
28484        }
28485        self.write_space();
28486        self.generate_expression(&e.expression)?;
28487        Ok(())
28488    }
28489
28490    fn generate_set_property(&mut self, e: &SetProperty) -> Result<()> {
28491        // SET or MULTISET
28492        if e.multi.is_some() {
28493            self.write_keyword("MULTISET");
28494        } else {
28495            self.write_keyword("SET");
28496        }
28497        Ok(())
28498    }
28499
28500    fn generate_settings_property(&mut self, e: &SettingsProperty) -> Result<()> {
28501        // SETTINGS expressions
28502        self.write_keyword("SETTINGS");
28503        if self.config.pretty && e.expressions.len() > 1 {
28504            // Pretty print: each setting on its own line, indented
28505            self.indent_level += 1;
28506            for (i, expr) in e.expressions.iter().enumerate() {
28507                if i > 0 {
28508                    self.write(",");
28509                }
28510                self.write_newline();
28511                self.write_indent();
28512                self.generate_expression(expr)?;
28513            }
28514            self.indent_level -= 1;
28515        } else {
28516            self.write_space();
28517            for (i, expr) in e.expressions.iter().enumerate() {
28518                if i > 0 {
28519                    self.write(", ");
28520                }
28521                self.generate_expression(expr)?;
28522            }
28523        }
28524        Ok(())
28525    }
28526
28527    fn generate_sharing_property(&mut self, e: &SharingProperty) -> Result<()> {
28528        // SHARING = this
28529        self.write_keyword("SHARING");
28530        if let Some(this) = &e.this {
28531            self.write(" = ");
28532            self.generate_expression(this)?;
28533        }
28534        Ok(())
28535    }
28536
28537    fn generate_slice(&mut self, e: &Slice) -> Result<()> {
28538        // Python array slicing: begin:end:step
28539        if let Some(begin) = &e.this {
28540            self.generate_expression(begin)?;
28541        }
28542        self.write(":");
28543        if let Some(end) = &e.expression {
28544            self.generate_expression(end)?;
28545        }
28546        if let Some(step) = &e.step {
28547            self.write(":");
28548            self.generate_expression(step)?;
28549        }
28550        Ok(())
28551    }
28552
28553    fn generate_sort_array(&mut self, e: &SortArray) -> Result<()> {
28554        // SORT_ARRAY(this, asc)
28555        self.write_keyword("SORT_ARRAY");
28556        self.write("(");
28557        self.generate_expression(&e.this)?;
28558        if let Some(asc) = &e.asc {
28559            self.write(", ");
28560            self.generate_expression(asc)?;
28561        }
28562        self.write(")");
28563        Ok(())
28564    }
28565
28566    fn generate_sort_by(&mut self, e: &SortBy) -> Result<()> {
28567        // SORT BY expressions
28568        self.write_keyword("SORT BY");
28569        self.write_space();
28570        for (i, expr) in e.expressions.iter().enumerate() {
28571            if i > 0 {
28572                self.write(", ");
28573            }
28574            self.generate_ordered(expr)?;
28575        }
28576        Ok(())
28577    }
28578
28579    fn generate_sort_key_property(&mut self, e: &SortKeyProperty) -> Result<()> {
28580        // [COMPOUND] SORTKEY(col1, col2, ...) - no space before paren
28581        if e.compound.is_some() {
28582            self.write_keyword("COMPOUND");
28583            self.write_space();
28584        }
28585        self.write_keyword("SORTKEY");
28586        self.write("(");
28587        // If this is a Tuple, unwrap its contents to avoid double parentheses
28588        if let Expression::Tuple(t) = e.this.as_ref() {
28589            for (i, expr) in t.expressions.iter().enumerate() {
28590                if i > 0 {
28591                    self.write(", ");
28592                }
28593                self.generate_expression(expr)?;
28594            }
28595        } else {
28596            self.generate_expression(&e.this)?;
28597        }
28598        self.write(")");
28599        Ok(())
28600    }
28601
28602    fn generate_split_part(&mut self, e: &SplitPart) -> Result<()> {
28603        // SPLIT_PART(this, delimiter, part_index)
28604        self.write_keyword("SPLIT_PART");
28605        self.write("(");
28606        self.generate_expression(&e.this)?;
28607        if let Some(delimiter) = &e.delimiter {
28608            self.write(", ");
28609            self.generate_expression(delimiter)?;
28610        }
28611        if let Some(part_index) = &e.part_index {
28612            self.write(", ");
28613            self.generate_expression(part_index)?;
28614        }
28615        self.write(")");
28616        Ok(())
28617    }
28618
28619    fn generate_sql_read_write_property(&mut self, e: &SqlReadWriteProperty) -> Result<()> {
28620        // READS SQL DATA or MODIFIES SQL DATA, etc.
28621        self.generate_expression(&e.this)?;
28622        Ok(())
28623    }
28624
28625    fn generate_sql_security_property(&mut self, e: &SqlSecurityProperty) -> Result<()> {
28626        // SQL SECURITY DEFINER or SQL SECURITY INVOKER
28627        self.write_keyword("SQL SECURITY");
28628        self.write_space();
28629        self.generate_expression(&e.this)?;
28630        Ok(())
28631    }
28632
28633    fn generate_st_distance(&mut self, e: &StDistance) -> Result<()> {
28634        // ST_DISTANCE(this, expression, [use_spheroid])
28635        self.write_keyword("ST_DISTANCE");
28636        self.write("(");
28637        self.generate_expression(&e.this)?;
28638        self.write(", ");
28639        self.generate_expression(&e.expression)?;
28640        if let Some(use_spheroid) = &e.use_spheroid {
28641            self.write(", ");
28642            self.generate_expression(use_spheroid)?;
28643        }
28644        self.write(")");
28645        Ok(())
28646    }
28647
28648    fn generate_st_point(&mut self, e: &StPoint) -> Result<()> {
28649        // ST_POINT(this, expression)
28650        self.write_keyword("ST_POINT");
28651        self.write("(");
28652        self.generate_expression(&e.this)?;
28653        self.write(", ");
28654        self.generate_expression(&e.expression)?;
28655        self.write(")");
28656        Ok(())
28657    }
28658
28659    fn generate_stability_property(&mut self, e: &StabilityProperty) -> Result<()> {
28660        // IMMUTABLE, STABLE, VOLATILE
28661        self.generate_expression(&e.this)?;
28662        Ok(())
28663    }
28664
28665    fn generate_standard_hash(&mut self, e: &StandardHash) -> Result<()> {
28666        // STANDARD_HASH(this, [expression])
28667        self.write_keyword("STANDARD_HASH");
28668        self.write("(");
28669        self.generate_expression(&e.this)?;
28670        if let Some(expression) = &e.expression {
28671            self.write(", ");
28672            self.generate_expression(expression)?;
28673        }
28674        self.write(")");
28675        Ok(())
28676    }
28677
28678    fn generate_storage_handler_property(&mut self, e: &StorageHandlerProperty) -> Result<()> {
28679        // STORED BY this
28680        self.write_keyword("STORED BY");
28681        self.write_space();
28682        self.generate_expression(&e.this)?;
28683        Ok(())
28684    }
28685
28686    fn generate_str_position(&mut self, e: &StrPosition) -> Result<()> {
28687        // STRPOS(this, substr) or STRPOS(this, substr, position)
28688        // Different dialects have different function names
28689        use crate::dialects::DialectType;
28690        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
28691            // Snowflake: CHARINDEX(substr, str[, position])
28692            self.write_keyword("CHARINDEX");
28693            self.write("(");
28694            if let Some(substr) = &e.substr {
28695                self.generate_expression(substr)?;
28696                self.write(", ");
28697            }
28698            self.generate_expression(&e.this)?;
28699            if let Some(position) = &e.position {
28700                self.write(", ");
28701                self.generate_expression(position)?;
28702            }
28703            self.write(")");
28704        } else if matches!(self.config.dialect, Some(DialectType::ClickHouse)) {
28705            self.write_keyword("POSITION");
28706            self.write("(");
28707            self.generate_expression(&e.this)?;
28708            if let Some(substr) = &e.substr {
28709                self.write(", ");
28710                self.generate_expression(substr)?;
28711            }
28712            if let Some(position) = &e.position {
28713                self.write(", ");
28714                self.generate_expression(position)?;
28715            }
28716            if let Some(occurrence) = &e.occurrence {
28717                self.write(", ");
28718                self.generate_expression(occurrence)?;
28719            }
28720            self.write(")");
28721        } else if matches!(self.config.dialect, Some(DialectType::SQLite) | Some(DialectType::Oracle) | Some(DialectType::BigQuery) | Some(DialectType::Teradata)) {
28722            self.write_keyword("INSTR");
28723            self.write("(");
28724            self.generate_expression(&e.this)?;
28725            if let Some(substr) = &e.substr {
28726                self.write(", ");
28727                self.generate_expression(substr)?;
28728            }
28729            if let Some(position) = &e.position {
28730                self.write(", ");
28731                self.generate_expression(position)?;
28732            } else if e.occurrence.is_some() {
28733                // INSTR requires a position arg before occurrence: INSTR(str, substr, start, nth)
28734                // Default start position is 1
28735                self.write(", 1");
28736            }
28737            if let Some(occurrence) = &e.occurrence {
28738                self.write(", ");
28739                self.generate_expression(occurrence)?;
28740            }
28741            self.write(")");
28742        } else if matches!(self.config.dialect, Some(DialectType::MySQL) | Some(DialectType::SingleStore) | Some(DialectType::Doris) | Some(DialectType::StarRocks)
28743            | Some(DialectType::Hive) | Some(DialectType::Spark) | Some(DialectType::Databricks)) {
28744            // LOCATE(substr, str[, position]) - substr first
28745            self.write_keyword("LOCATE");
28746            self.write("(");
28747            if let Some(substr) = &e.substr {
28748                self.generate_expression(substr)?;
28749                self.write(", ");
28750            }
28751            self.generate_expression(&e.this)?;
28752            if let Some(position) = &e.position {
28753                self.write(", ");
28754                self.generate_expression(position)?;
28755            }
28756            self.write(")");
28757        } else if matches!(self.config.dialect, Some(DialectType::TSQL)) {
28758            // CHARINDEX(substr, str[, position])
28759            self.write_keyword("CHARINDEX");
28760            self.write("(");
28761            if let Some(substr) = &e.substr {
28762                self.generate_expression(substr)?;
28763                self.write(", ");
28764            }
28765            self.generate_expression(&e.this)?;
28766            if let Some(position) = &e.position {
28767                self.write(", ");
28768                self.generate_expression(position)?;
28769            }
28770            self.write(")");
28771        } else {
28772            self.write_keyword("STRPOS");
28773            self.write("(");
28774            self.generate_expression(&e.this)?;
28775            if let Some(substr) = &e.substr {
28776                self.write(", ");
28777                self.generate_expression(substr)?;
28778            }
28779            if let Some(position) = &e.position {
28780                self.write(", ");
28781                self.generate_expression(position)?;
28782            }
28783            if let Some(occurrence) = &e.occurrence {
28784                self.write(", ");
28785                self.generate_expression(occurrence)?;
28786            }
28787            self.write(")");
28788        }
28789        Ok(())
28790    }
28791
28792    fn generate_str_to_date(&mut self, e: &StrToDate) -> Result<()> {
28793        match self.config.dialect {
28794            Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive) => {
28795                // TO_DATE(this, java_format)
28796                self.write_keyword("TO_DATE");
28797                self.write("(");
28798                self.generate_expression(&e.this)?;
28799                if let Some(format) = &e.format {
28800                    self.write(", '");
28801                    self.write(&Self::strftime_to_java_format(format));
28802                    self.write("'");
28803                }
28804                self.write(")");
28805            }
28806            Some(DialectType::DuckDB) => {
28807                // CAST(STRPTIME(this, format) AS DATE)
28808                self.write_keyword("CAST");
28809                self.write("(");
28810                self.write_keyword("STRPTIME");
28811                self.write("(");
28812                self.generate_expression(&e.this)?;
28813                if let Some(format) = &e.format {
28814                    self.write(", '");
28815                    self.write(format);
28816                    self.write("'");
28817                }
28818                self.write(")");
28819                self.write_keyword(" AS ");
28820                self.write_keyword("DATE");
28821                self.write(")");
28822            }
28823            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => {
28824                // TO_DATE(this, pg_format)
28825                self.write_keyword("TO_DATE");
28826                self.write("(");
28827                self.generate_expression(&e.this)?;
28828                if let Some(format) = &e.format {
28829                    self.write(", '");
28830                    self.write(&Self::strftime_to_postgres_format(format));
28831                    self.write("'");
28832                }
28833                self.write(")");
28834            }
28835            Some(DialectType::BigQuery) => {
28836                // PARSE_DATE(format, this) - note: format comes first for BigQuery
28837                self.write_keyword("PARSE_DATE");
28838                self.write("(");
28839                if let Some(format) = &e.format {
28840                    self.write("'");
28841                    self.write(format);
28842                    self.write("'");
28843                    self.write(", ");
28844                }
28845                self.generate_expression(&e.this)?;
28846                self.write(")");
28847            }
28848            Some(DialectType::Teradata) => {
28849                // CAST(this AS DATE FORMAT 'teradata_fmt')
28850                self.write_keyword("CAST");
28851                self.write("(");
28852                self.generate_expression(&e.this)?;
28853                self.write_keyword(" AS ");
28854                self.write_keyword("DATE");
28855                if let Some(format) = &e.format {
28856                    self.write_keyword(" FORMAT ");
28857                    self.write("'");
28858                    self.write(&Self::strftime_to_teradata_format(format));
28859                    self.write("'");
28860                }
28861                self.write(")");
28862            }
28863            _ => {
28864                // STR_TO_DATE(this, format) - MySQL default
28865                self.write_keyword("STR_TO_DATE");
28866                self.write("(");
28867                self.generate_expression(&e.this)?;
28868                if let Some(format) = &e.format {
28869                    self.write(", '");
28870                    self.write(format);
28871                    self.write("'");
28872                }
28873                self.write(")");
28874            }
28875        }
28876        Ok(())
28877    }
28878
28879    /// Convert strftime format to Teradata date format (YYYY, DD, MM, etc.)
28880    fn strftime_to_teradata_format(fmt: &str) -> String {
28881        let mut result = fmt.to_string();
28882        result = result.replace("%Y", "YYYY");
28883        result = result.replace("%y", "YY");
28884        result = result.replace("%m", "MM");
28885        result = result.replace("%B", "MMMM");
28886        result = result.replace("%b", "MMM");
28887        result = result.replace("%d", "DD");
28888        result = result.replace("%j", "DDD");
28889        result = result.replace("%H", "HH");
28890        result = result.replace("%M", "MI");
28891        result = result.replace("%S", "SS");
28892        result = result.replace("%f", "SSSSSS");
28893        result = result.replace("%A", "EEEE");
28894        result = result.replace("%a", "EEE");
28895        result
28896    }
28897
28898    /// Convert strftime format (%Y, %m, %d, etc.) to Java date format (yyyy, MM, dd, etc.)
28899    fn strftime_to_java_format(fmt: &str) -> String {
28900        let mut result = fmt.to_string();
28901        result = result.replace("%Y", "yyyy");
28902        result = result.replace("%y", "yy");
28903        result = result.replace("%m", "MM");
28904        result = result.replace("%B", "MMMM");
28905        result = result.replace("%b", "MMM");
28906        result = result.replace("%d", "dd");
28907        result = result.replace("%j", "DDD");
28908        result = result.replace("%H", "HH");
28909        result = result.replace("%M", "mm");
28910        result = result.replace("%S", "ss");
28911        result = result.replace("%f", "SSSSSS");
28912        result = result.replace("%A", "EEEE");
28913        result = result.replace("%a", "EEE");
28914        result
28915    }
28916
28917    /// Convert strftime format to PostgreSQL date format (YYYY, MM, DD, etc.)
28918    fn strftime_to_postgres_format(fmt: &str) -> String {
28919        let mut result = fmt.to_string();
28920        result = result.replace("%Y", "YYYY");
28921        result = result.replace("%y", "YY");
28922        result = result.replace("%m", "MM");
28923        result = result.replace("%B", "Month");
28924        result = result.replace("%b", "Mon");
28925        result = result.replace("%d", "DD");
28926        result = result.replace("%j", "DDD");
28927        result = result.replace("%H", "HH24");
28928        result = result.replace("%M", "MI");
28929        result = result.replace("%S", "SS");
28930        result = result.replace("%f", "US");
28931        result = result.replace("%A", "Day");
28932        result = result.replace("%a", "Dy");
28933        result
28934    }
28935
28936    fn generate_str_to_map(&mut self, e: &StrToMap) -> Result<()> {
28937        // STR_TO_MAP(this, pair_delim, key_value_delim)
28938        self.write_keyword("STR_TO_MAP");
28939        self.write("(");
28940        self.generate_expression(&e.this)?;
28941        // Spark/Hive: STR_TO_MAP needs explicit default delimiters
28942        let needs_defaults = matches!(
28943            self.config.dialect,
28944            Some(DialectType::Spark) | Some(DialectType::Hive) | Some(DialectType::Databricks)
28945        );
28946        if let Some(pair_delim) = &e.pair_delim {
28947            self.write(", ");
28948            self.generate_expression(pair_delim)?;
28949        } else if needs_defaults {
28950            self.write(", ','");
28951        }
28952        if let Some(key_value_delim) = &e.key_value_delim {
28953            self.write(", ");
28954            self.generate_expression(key_value_delim)?;
28955        } else if needs_defaults {
28956            self.write(", ':'");
28957        }
28958        self.write(")");
28959        Ok(())
28960    }
28961
28962    fn generate_str_to_time(&mut self, e: &StrToTime) -> Result<()> {
28963        match self.config.dialect {
28964            Some(DialectType::Exasol) => {
28965                self.write_keyword("TO_DATE");
28966                self.write("(");
28967                self.generate_expression(&e.this)?;
28968                self.write(", '");
28969                self.write(&Self::convert_strptime_to_exasol_format(&e.format));
28970                self.write("'");
28971                self.write(")");
28972            }
28973            Some(DialectType::BigQuery) => {
28974                // BigQuery: PARSE_TIMESTAMP(format, value) - note swapped args
28975                let fmt = Self::snowflake_format_to_strftime(&e.format);
28976                // BigQuery normalizes: %Y-%m-%d -> %F, %H:%M:%S -> %T
28977                let fmt = fmt.replace("%Y-%m-%d", "%F").replace("%H:%M:%S", "%T");
28978                self.write_keyword("PARSE_TIMESTAMP");
28979                self.write("('");
28980                self.write(&fmt);
28981                self.write("', ");
28982                self.generate_expression(&e.this)?;
28983                self.write(")");
28984            }
28985            Some(DialectType::Spark) | Some(DialectType::Databricks) => {
28986                // Spark: TO_TIMESTAMP(value, format)
28987                let fmt = Self::snowflake_format_to_spark(&e.format);
28988                self.write_keyword("TO_TIMESTAMP");
28989                self.write("(");
28990                self.generate_expression(&e.this)?;
28991                self.write(", '");
28992                self.write(&fmt);
28993                self.write("')");
28994            }
28995            Some(DialectType::Presto) | Some(DialectType::Trino) => {
28996                // Presto: DATE_PARSE(value, format)
28997                let fmt = Self::snowflake_format_to_strftime(&e.format);
28998                self.write_keyword("DATE_PARSE");
28999                self.write("(");
29000                self.generate_expression(&e.this)?;
29001                self.write(", '");
29002                self.write(&fmt);
29003                self.write("')");
29004            }
29005            Some(DialectType::DuckDB) => {
29006                // DuckDB: STRPTIME(value, format)
29007                let fmt = Self::snowflake_format_to_strftime(&e.format);
29008                self.write_keyword("STRPTIME");
29009                self.write("(");
29010                self.generate_expression(&e.this)?;
29011                self.write(", '");
29012                self.write(&fmt);
29013                self.write("')");
29014            }
29015            Some(DialectType::Snowflake) => {
29016                // Snowflake: TO_TIMESTAMP(value, format)
29017                self.write_keyword("TO_TIMESTAMP");
29018                self.write("(");
29019                self.generate_expression(&e.this)?;
29020                self.write(", '");
29021                self.write(&e.format);
29022                self.write("')");
29023            }
29024            _ => {
29025                // Default: STR_TO_TIME(this, format)
29026                self.write_keyword("STR_TO_TIME");
29027                self.write("(");
29028                self.generate_expression(&e.this)?;
29029                self.write(", '");
29030                self.write(&e.format);
29031                self.write("'");
29032                self.write(")");
29033            }
29034        }
29035        Ok(())
29036    }
29037
29038    /// Convert Snowflake normalized format to strftime-style (%Y, %m, etc.)
29039    fn snowflake_format_to_strftime(format: &str) -> String {
29040        let mut result = String::new();
29041        let chars: Vec<char> = format.chars().collect();
29042        let mut i = 0;
29043        while i < chars.len() {
29044            let remaining = &format[i..];
29045            if remaining.starts_with("yyyy") {
29046                result.push_str("%Y");
29047                i += 4;
29048            } else if remaining.starts_with("yy") {
29049                result.push_str("%y");
29050                i += 2;
29051            } else if remaining.starts_with("mmmm") {
29052                result.push_str("%B"); // full month name
29053                i += 4;
29054            } else if remaining.starts_with("mon") {
29055                result.push_str("%b"); // abbreviated month
29056                i += 3;
29057            } else if remaining.starts_with("mm") {
29058                result.push_str("%m");
29059                i += 2;
29060            } else if remaining.starts_with("DD") {
29061                result.push_str("%d");
29062                i += 2;
29063            } else if remaining.starts_with("dy") {
29064                result.push_str("%a"); // abbreviated day name
29065                i += 2;
29066            } else if remaining.starts_with("hh24") {
29067                result.push_str("%H");
29068                i += 4;
29069            } else if remaining.starts_with("hh12") {
29070                result.push_str("%I");
29071                i += 4;
29072            } else if remaining.starts_with("hh") {
29073                result.push_str("%H");
29074                i += 2;
29075            } else if remaining.starts_with("mi") {
29076                result.push_str("%M");
29077                i += 2;
29078            } else if remaining.starts_with("ss") {
29079                result.push_str("%S");
29080                i += 2;
29081            } else if remaining.starts_with("ff") {
29082                // Fractional seconds
29083                result.push_str("%f");
29084                i += 2;
29085                // Skip digits after ff (ff3, ff6, ff9)
29086                while i < chars.len() && chars[i].is_ascii_digit() {
29087                    i += 1;
29088                }
29089            } else if remaining.starts_with("am") || remaining.starts_with("pm") {
29090                result.push_str("%p");
29091                i += 2;
29092            } else if remaining.starts_with("tz") {
29093                result.push_str("%Z");
29094                i += 2;
29095            } else {
29096                result.push(chars[i]);
29097                i += 1;
29098            }
29099        }
29100        result
29101    }
29102
29103    /// Convert Snowflake normalized format to Spark format (Java-style)
29104    fn snowflake_format_to_spark(format: &str) -> String {
29105        let mut result = String::new();
29106        let chars: Vec<char> = format.chars().collect();
29107        let mut i = 0;
29108        while i < chars.len() {
29109            let remaining = &format[i..];
29110            if remaining.starts_with("yyyy") {
29111                result.push_str("yyyy");
29112                i += 4;
29113            } else if remaining.starts_with("yy") {
29114                result.push_str("yy");
29115                i += 2;
29116            } else if remaining.starts_with("mmmm") {
29117                result.push_str("MMMM"); // full month name
29118                i += 4;
29119            } else if remaining.starts_with("mon") {
29120                result.push_str("MMM"); // abbreviated month
29121                i += 3;
29122            } else if remaining.starts_with("mm") {
29123                result.push_str("MM");
29124                i += 2;
29125            } else if remaining.starts_with("DD") {
29126                result.push_str("dd");
29127                i += 2;
29128            } else if remaining.starts_with("dy") {
29129                result.push_str("EEE"); // abbreviated day name
29130                i += 2;
29131            } else if remaining.starts_with("hh24") {
29132                result.push_str("HH");
29133                i += 4;
29134            } else if remaining.starts_with("hh12") {
29135                result.push_str("hh");
29136                i += 4;
29137            } else if remaining.starts_with("hh") {
29138                result.push_str("HH");
29139                i += 2;
29140            } else if remaining.starts_with("mi") {
29141                result.push_str("mm");
29142                i += 2;
29143            } else if remaining.starts_with("ss") {
29144                result.push_str("ss");
29145                i += 2;
29146            } else if remaining.starts_with("ff") {
29147                result.push_str("SSS"); // milliseconds
29148                i += 2;
29149                // Skip digits after ff
29150                while i < chars.len() && chars[i].is_ascii_digit() {
29151                    i += 1;
29152                }
29153            } else if remaining.starts_with("am") || remaining.starts_with("pm") {
29154                result.push_str("a");
29155                i += 2;
29156            } else if remaining.starts_with("tz") {
29157                result.push_str("z");
29158                i += 2;
29159            } else {
29160                result.push(chars[i]);
29161                i += 1;
29162            }
29163        }
29164        result
29165    }
29166
29167    fn generate_str_to_unix(&mut self, e: &StrToUnix) -> Result<()> {
29168        // STR_TO_UNIX(this, format)
29169        self.write_keyword("STR_TO_UNIX");
29170        self.write("(");
29171        if let Some(this) = &e.this {
29172            self.generate_expression(this)?;
29173        }
29174        if let Some(format) = &e.format {
29175            self.write(", '");
29176            self.write(format);
29177            self.write("'");
29178        }
29179        self.write(")");
29180        Ok(())
29181    }
29182
29183    fn generate_string_to_array(&mut self, e: &StringToArray) -> Result<()> {
29184        // STRING_TO_ARRAY(this, delimiter, null_string)
29185        self.write_keyword("STRING_TO_ARRAY");
29186        self.write("(");
29187        self.generate_expression(&e.this)?;
29188        if let Some(expression) = &e.expression {
29189            self.write(", ");
29190            self.generate_expression(expression)?;
29191        }
29192        if let Some(null_val) = &e.null {
29193            self.write(", ");
29194            self.generate_expression(null_val)?;
29195        }
29196        self.write(")");
29197        Ok(())
29198    }
29199
29200    fn generate_struct(&mut self, e: &Struct) -> Result<()> {
29201        if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
29202            // Snowflake: OBJECT_CONSTRUCT('key', value, 'key', value, ...)
29203            self.write_keyword("OBJECT_CONSTRUCT");
29204            self.write("(");
29205            for (i, (name, expr)) in e.fields.iter().enumerate() {
29206                if i > 0 {
29207                    self.write(", ");
29208                }
29209                if let Some(name) = name {
29210                    self.write("'");
29211                    self.write(name);
29212                    self.write("'");
29213                    self.write(", ");
29214                } else {
29215                    self.write("'_");
29216                    self.write(&i.to_string());
29217                    self.write("'");
29218                    self.write(", ");
29219                }
29220                self.generate_expression(expr)?;
29221            }
29222            self.write(")");
29223        } else if self.config.struct_curly_brace_notation {
29224            // DuckDB-style: {'key': value, ...}
29225            self.write("{");
29226            for (i, (name, expr)) in e.fields.iter().enumerate() {
29227                if i > 0 {
29228                    self.write(", ");
29229                }
29230                if let Some(name) = name {
29231                    // Quote the key as a string literal
29232                    self.write("'");
29233                    self.write(name);
29234                    self.write("'");
29235                    self.write(": ");
29236                } else {
29237                    // Unnamed field: use positional key
29238                    self.write("'_");
29239                    self.write(&i.to_string());
29240                    self.write("'");
29241                    self.write(": ");
29242                }
29243                self.generate_expression(expr)?;
29244            }
29245            self.write("}");
29246        } else {
29247            // Standard SQL struct notation
29248            // BigQuery/Spark/Databricks use: STRUCT(value AS name, ...)
29249            // Others (Presto etc.) use: STRUCT(name AS value, ...) or ROW(value, ...)
29250            let value_as_name = matches!(
29251                self.config.dialect,
29252                Some(DialectType::BigQuery)
29253                | Some(DialectType::Spark)
29254               
29255                | Some(DialectType::Databricks)
29256                | Some(DialectType::Hive)
29257            );
29258            self.write_keyword("STRUCT");
29259            self.write("(");
29260            for (i, (name, expr)) in e.fields.iter().enumerate() {
29261                if i > 0 {
29262                    self.write(", ");
29263                }
29264                if let Some(name) = name {
29265                    if value_as_name {
29266                        // STRUCT(value AS name)
29267                        self.generate_expression(expr)?;
29268                        self.write_space();
29269                        self.write_keyword("AS");
29270                        self.write_space();
29271                        // Quote name if it contains spaces or special chars
29272                        let needs_quoting = name.contains(' ') || name.contains('-');
29273                        if needs_quoting {
29274                            if matches!(self.config.dialect, Some(DialectType::Spark) | Some(DialectType::Databricks) | Some(DialectType::Hive)) {
29275                                self.write("`");
29276                                self.write(name);
29277                                self.write("`");
29278                            } else {
29279                                self.write(name);
29280                            }
29281                        } else {
29282                            self.write(name);
29283                        }
29284                    } else {
29285                        // STRUCT(name AS value)
29286                        self.write(name);
29287                        self.write_space();
29288                        self.write_keyword("AS");
29289                        self.write_space();
29290                        self.generate_expression(expr)?;
29291                    }
29292                } else {
29293                    self.generate_expression(expr)?;
29294                }
29295            }
29296            self.write(")");
29297        }
29298        Ok(())
29299    }
29300
29301    fn generate_stuff(&mut self, e: &Stuff) -> Result<()> {
29302        // STUFF(this, start, length, expression)
29303        self.write_keyword("STUFF");
29304        self.write("(");
29305        self.generate_expression(&e.this)?;
29306        if let Some(start) = &e.start {
29307            self.write(", ");
29308            self.generate_expression(start)?;
29309        }
29310        if let Some(length) = e.length {
29311            self.write(", ");
29312            self.write(&length.to_string());
29313        }
29314        self.write(", ");
29315        self.generate_expression(&e.expression)?;
29316        self.write(")");
29317        Ok(())
29318    }
29319
29320    fn generate_substring_index(&mut self, e: &SubstringIndex) -> Result<()> {
29321        // SUBSTRING_INDEX(this, delimiter, count)
29322        self.write_keyword("SUBSTRING_INDEX");
29323        self.write("(");
29324        self.generate_expression(&e.this)?;
29325        if let Some(delimiter) = &e.delimiter {
29326            self.write(", ");
29327            self.generate_expression(delimiter)?;
29328        }
29329        if let Some(count) = &e.count {
29330            self.write(", ");
29331            self.generate_expression(count)?;
29332        }
29333        self.write(")");
29334        Ok(())
29335    }
29336
29337    fn generate_summarize(&mut self, e: &Summarize) -> Result<()> {
29338        // SUMMARIZE [TABLE] this
29339        self.write_keyword("SUMMARIZE");
29340        if e.table.is_some() {
29341            self.write_space();
29342            self.write_keyword("TABLE");
29343        }
29344        self.write_space();
29345        self.generate_expression(&e.this)?;
29346        Ok(())
29347    }
29348
29349    fn generate_systimestamp(&mut self, _e: &Systimestamp) -> Result<()> {
29350        // SYSTIMESTAMP
29351        self.write_keyword("SYSTIMESTAMP");
29352        Ok(())
29353    }
29354
29355    fn generate_table_alias(&mut self, e: &TableAlias) -> Result<()> {
29356        // alias (columns...)
29357        if let Some(this) = &e.this {
29358            self.generate_expression(this)?;
29359        }
29360        if !e.columns.is_empty() {
29361            self.write("(");
29362            for (i, col) in e.columns.iter().enumerate() {
29363                if i > 0 {
29364                    self.write(", ");
29365                }
29366                self.generate_expression(col)?;
29367            }
29368            self.write(")");
29369        }
29370        Ok(())
29371    }
29372
29373    fn generate_table_from_rows(&mut self, e: &TableFromRows) -> Result<()> {
29374        // TABLE(this) [AS alias]
29375        self.write_keyword("TABLE");
29376        self.write("(");
29377        self.generate_expression(&e.this)?;
29378        self.write(")");
29379        if let Some(alias) = &e.alias {
29380            self.write_space();
29381            self.write_keyword("AS");
29382            self.write_space();
29383            self.write(alias);
29384        }
29385        Ok(())
29386    }
29387
29388    fn generate_rows_from(&mut self, e: &RowsFrom) -> Result<()> {
29389        // ROWS FROM (func1(...) AS alias1(...), func2(...) AS alias2(...)) [WITH ORDINALITY] [AS alias(...)]
29390        self.write_keyword("ROWS FROM");
29391        self.write(" (");
29392        for (i, expr) in e.expressions.iter().enumerate() {
29393            if i > 0 {
29394                self.write(", ");
29395            }
29396            // Each expression is either:
29397            // - A plain function (no alias)
29398            // - A Tuple(function, TableAlias) for: FUNC() AS alias(col type, ...)
29399            match expr {
29400                Expression::Tuple(tuple) if tuple.expressions.len() == 2 => {
29401                    // First element is the function, second is the TableAlias
29402                    self.generate_expression(&tuple.expressions[0])?;
29403                    self.write_space();
29404                    self.write_keyword("AS");
29405                    self.write_space();
29406                    self.generate_expression(&tuple.expressions[1])?;
29407                }
29408                _ => {
29409                    self.generate_expression(expr)?;
29410                }
29411            }
29412        }
29413        self.write(")");
29414        if e.ordinality {
29415            self.write_space();
29416            self.write_keyword("WITH ORDINALITY");
29417        }
29418        if let Some(alias) = &e.alias {
29419            self.write_space();
29420            self.write_keyword("AS");
29421            self.write_space();
29422            self.generate_expression(alias)?;
29423        }
29424        Ok(())
29425    }
29426
29427    fn generate_table_sample(&mut self, e: &TableSample) -> Result<()> {
29428        use crate::dialects::DialectType;
29429
29430        // New wrapper pattern: expression + Sample struct
29431        if let (Some(this), Some(sample)) = (&e.this, &e.sample) {
29432            // For alias_post_tablesample dialects (Spark, Hive, Oracle): output base expr, TABLESAMPLE, then alias
29433            if self.config.alias_post_tablesample {
29434                // Handle Subquery with alias and Alias wrapper
29435                if let Expression::Subquery(ref s) = **this {
29436                    if let Some(ref alias) = s.alias {
29437                        // Create a clone without alias for output
29438                        let mut subquery_no_alias = (**s).clone();
29439                        subquery_no_alias.alias = None;
29440                        subquery_no_alias.column_aliases = Vec::new();
29441                        self.generate_expression(&Expression::Subquery(Box::new(subquery_no_alias)))?;
29442                        self.write_space();
29443                        self.write_keyword("TABLESAMPLE");
29444                        self.generate_sample_body(sample)?;
29445                        if let Some(ref seed) = sample.seed {
29446                            self.write_space();
29447                            let use_seed = sample.use_seed_keyword
29448                                && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29449                            if use_seed { self.write_keyword("SEED"); } else { self.write_keyword("REPEATABLE"); }
29450                            self.write(" (");
29451                            self.generate_expression(seed)?;
29452                            self.write(")");
29453                        }
29454                        self.write_space();
29455                        self.write_keyword("AS");
29456                        self.write_space();
29457                        self.generate_identifier(alias)?;
29458                        return Ok(());
29459                    }
29460                } else if let Expression::Alias(ref a) = **this {
29461                    // Output the base expression without alias
29462                    self.generate_expression(&a.this)?;
29463                    self.write_space();
29464                    self.write_keyword("TABLESAMPLE");
29465                    self.generate_sample_body(sample)?;
29466                    if let Some(ref seed) = sample.seed {
29467                        self.write_space();
29468                        let use_seed = sample.use_seed_keyword
29469                            && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29470                        if use_seed { self.write_keyword("SEED"); } else { self.write_keyword("REPEATABLE"); }
29471                        self.write(" (");
29472                        self.generate_expression(seed)?;
29473                        self.write(")");
29474                    }
29475                    // Output alias after TABLESAMPLE
29476                    self.write_space();
29477                    self.write_keyword("AS");
29478                    self.write_space();
29479                    self.generate_identifier(&a.alias)?;
29480                    return Ok(());
29481                }
29482            }
29483            // Default: generate wrapped expression first, then TABLESAMPLE
29484            self.generate_expression(this)?;
29485            self.write_space();
29486            self.write_keyword("TABLESAMPLE");
29487            self.generate_sample_body(sample)?;
29488            // Seed for table-level sample
29489            if let Some(ref seed) = sample.seed {
29490                self.write_space();
29491                // Databricks uses REPEATABLE, not SEED
29492                let use_seed = sample.use_seed_keyword
29493                    && !matches!(self.config.dialect, Some(crate::dialects::DialectType::Databricks) | Some(crate::dialects::DialectType::Spark));
29494                if use_seed {
29495                    self.write_keyword("SEED");
29496                } else {
29497                    self.write_keyword("REPEATABLE");
29498                }
29499                self.write(" (");
29500                self.generate_expression(seed)?;
29501                self.write(")");
29502            }
29503            return Ok(());
29504        }
29505
29506        // Legacy pattern: TABLESAMPLE [method] (expressions) or TABLESAMPLE method BUCKET numerator OUT OF denominator
29507        self.write_keyword("TABLESAMPLE");
29508        if let Some(method) = &e.method {
29509            self.write_space();
29510            self.write_keyword(method);
29511        } else if matches!(self.config.dialect, Some(DialectType::Snowflake)) {
29512            // Snowflake defaults to BERNOULLI when no method is specified
29513            self.write_space();
29514            self.write_keyword("BERNOULLI");
29515        }
29516        if let (Some(numerator), Some(denominator)) = (&e.bucket_numerator, &e.bucket_denominator) {
29517            self.write_space();
29518            self.write_keyword("BUCKET");
29519            self.write_space();
29520            self.generate_expression(numerator)?;
29521            self.write_space();
29522            self.write_keyword("OUT OF");
29523            self.write_space();
29524            self.generate_expression(denominator)?;
29525            if let Some(field) = &e.bucket_field {
29526                self.write_space();
29527                self.write_keyword("ON");
29528                self.write_space();
29529                self.generate_expression(field)?;
29530            }
29531        } else if !e.expressions.is_empty() {
29532            self.write(" (");
29533            for (i, expr) in e.expressions.iter().enumerate() {
29534                if i > 0 {
29535                    self.write(", ");
29536                }
29537                self.generate_expression(expr)?;
29538            }
29539            self.write(")");
29540        } else if let Some(percent) = &e.percent {
29541            self.write(" (");
29542            self.generate_expression(percent)?;
29543            self.write_space();
29544            self.write_keyword("PERCENT");
29545            self.write(")");
29546        }
29547        Ok(())
29548    }
29549
29550    fn generate_tag(&mut self, e: &Tag) -> Result<()> {
29551        // [prefix]this[postfix]
29552        if let Some(prefix) = &e.prefix {
29553            self.generate_expression(prefix)?;
29554        }
29555        if let Some(this) = &e.this {
29556            self.generate_expression(this)?;
29557        }
29558        if let Some(postfix) = &e.postfix {
29559            self.generate_expression(postfix)?;
29560        }
29561        Ok(())
29562    }
29563
29564    fn generate_tags(&mut self, e: &Tags) -> Result<()> {
29565        // TAG (expressions)
29566        self.write_keyword("TAG");
29567        self.write(" (");
29568        for (i, expr) in e.expressions.iter().enumerate() {
29569            if i > 0 {
29570                self.write(", ");
29571            }
29572            self.generate_expression(expr)?;
29573        }
29574        self.write(")");
29575        Ok(())
29576    }
29577
29578    fn generate_temporary_property(&mut self, e: &TemporaryProperty) -> Result<()> {
29579        // TEMPORARY or TEMP or [this] TEMPORARY
29580        if let Some(this) = &e.this {
29581            self.generate_expression(this)?;
29582            self.write_space();
29583        }
29584        self.write_keyword("TEMPORARY");
29585        Ok(())
29586    }
29587
29588    /// Generate a Time function expression
29589    /// For most dialects: TIME('value')
29590    fn generate_time_func(&mut self, e: &UnaryFunc) -> Result<()> {
29591        // Standard: TIME(value)
29592        self.write_keyword("TIME");
29593        self.write("(");
29594        self.generate_expression(&e.this)?;
29595        self.write(")");
29596        Ok(())
29597    }
29598
29599    fn generate_time_add(&mut self, e: &TimeAdd) -> Result<()> {
29600        // TIME_ADD(this, expression, unit)
29601        self.write_keyword("TIME_ADD");
29602        self.write("(");
29603        self.generate_expression(&e.this)?;
29604        self.write(", ");
29605        self.generate_expression(&e.expression)?;
29606        if let Some(unit) = &e.unit {
29607            self.write(", ");
29608            self.write_keyword(unit);
29609        }
29610        self.write(")");
29611        Ok(())
29612    }
29613
29614    fn generate_time_diff(&mut self, e: &TimeDiff) -> Result<()> {
29615        // TIME_DIFF(this, expression, unit)
29616        self.write_keyword("TIME_DIFF");
29617        self.write("(");
29618        self.generate_expression(&e.this)?;
29619        self.write(", ");
29620        self.generate_expression(&e.expression)?;
29621        if let Some(unit) = &e.unit {
29622            self.write(", ");
29623            self.write_keyword(unit);
29624        }
29625        self.write(")");
29626        Ok(())
29627    }
29628
29629    fn generate_time_from_parts(&mut self, e: &TimeFromParts) -> Result<()> {
29630        // TIME_FROM_PARTS(hour, minute, second, nanosecond)
29631        self.write_keyword("TIME_FROM_PARTS");
29632        self.write("(");
29633        let mut first = true;
29634        if let Some(hour) = &e.hour {
29635            self.generate_expression(hour)?;
29636            first = false;
29637        }
29638        if let Some(minute) = &e.min {
29639            if !first { self.write(", "); }
29640            self.generate_expression(minute)?;
29641            first = false;
29642        }
29643        if let Some(second) = &e.sec {
29644            if !first { self.write(", "); }
29645            self.generate_expression(second)?;
29646            first = false;
29647        }
29648        if let Some(ns) = &e.nano {
29649            if !first { self.write(", "); }
29650            self.generate_expression(ns)?;
29651        }
29652        self.write(")");
29653        Ok(())
29654    }
29655
29656    fn generate_time_slice(&mut self, e: &TimeSlice) -> Result<()> {
29657        // TIME_SLICE(this, expression, unit)
29658        self.write_keyword("TIME_SLICE");
29659        self.write("(");
29660        self.generate_expression(&e.this)?;
29661        self.write(", ");
29662        self.generate_expression(&e.expression)?;
29663        self.write(", ");
29664        self.write_keyword(&e.unit);
29665        self.write(")");
29666        Ok(())
29667    }
29668
29669    fn generate_time_str_to_time(&mut self, e: &TimeStrToTime) -> Result<()> {
29670        // TIME_STR_TO_TIME(this)
29671        self.write_keyword("TIME_STR_TO_TIME");
29672        self.write("(");
29673        self.generate_expression(&e.this)?;
29674        self.write(")");
29675        Ok(())
29676    }
29677
29678    fn generate_time_sub(&mut self, e: &TimeSub) -> Result<()> {
29679        // TIME_SUB(this, expression, unit)
29680        self.write_keyword("TIME_SUB");
29681        self.write("(");
29682        self.generate_expression(&e.this)?;
29683        self.write(", ");
29684        self.generate_expression(&e.expression)?;
29685        if let Some(unit) = &e.unit {
29686            self.write(", ");
29687            self.write_keyword(unit);
29688        }
29689        self.write(")");
29690        Ok(())
29691    }
29692
29693    fn generate_time_to_str(&mut self, e: &TimeToStr) -> Result<()> {
29694        // Exasol uses TO_CHAR with Exasol-specific format
29695        if self.config.dialect == Some(DialectType::Exasol) {
29696            self.write_keyword("TO_CHAR");
29697            self.write("(");
29698            self.generate_expression(&e.this)?;
29699            self.write(", '");
29700            self.write(&Self::convert_strptime_to_exasol_format(&e.format));
29701            self.write("'");
29702            self.write(")");
29703            return Ok(());
29704        }
29705
29706        // PostgreSQL/Redshift uses TO_CHAR with PG-specific format
29707        if matches!(self.config.dialect, Some(DialectType::PostgreSQL) | Some(DialectType::Redshift)) {
29708            self.write_keyword("TO_CHAR");
29709            self.write("(");
29710            self.generate_expression(&e.this)?;
29711            self.write(", '");
29712            self.write(&Self::convert_strptime_to_postgres_format(&e.format));
29713            self.write("'");
29714            self.write(")");
29715            return Ok(());
29716        }
29717
29718        // TIME_TO_STR(this, format)
29719        self.write_keyword("TIME_TO_STR");
29720        self.write("(");
29721        self.generate_expression(&e.this)?;
29722        self.write(", '");
29723        self.write(&e.format);
29724        self.write("'");
29725        self.write(")");
29726        Ok(())
29727    }
29728
29729    fn generate_time_trunc(&mut self, e: &TimeTrunc) -> Result<()> {
29730        // TIME_TRUNC(this, unit)
29731        self.write_keyword("TIME_TRUNC");
29732        self.write("(");
29733        self.generate_expression(&e.this)?;
29734        self.write(", ");
29735        self.write_keyword(&e.unit);
29736        self.write(")");
29737        Ok(())
29738    }
29739
29740    fn generate_time_unit(&mut self, e: &TimeUnit) -> Result<()> {
29741        // Just output the unit name
29742        if let Some(unit) = &e.unit {
29743            self.write_keyword(unit);
29744        }
29745        Ok(())
29746    }
29747
29748    /// Generate a Timestamp function expression
29749    /// For Exasol: {ts'value'} -> TO_TIMESTAMP('value')
29750    /// For other dialects: TIMESTAMP('value')
29751    fn generate_timestamp_func(&mut self, e: &TimestampFunc) -> Result<()> {
29752        use crate::dialects::DialectType;
29753        use crate::expressions::Literal;
29754
29755        match self.config.dialect {
29756            // Exasol uses TO_TIMESTAMP for Timestamp expressions
29757            Some(DialectType::Exasol) => {
29758                self.write_keyword("TO_TIMESTAMP");
29759                self.write("(");
29760                // Extract the string value from the expression if it's a string literal
29761                if let Some(this) = &e.this {
29762                    match this.as_ref() {
29763                        Expression::Literal(Literal::String(s)) => {
29764                            self.write("'");
29765                            self.write(s);
29766                            self.write("'");
29767                        }
29768                        _ => {
29769                            self.generate_expression(this)?;
29770                        }
29771                    }
29772                }
29773                self.write(")");
29774            }
29775            // Standard: TIMESTAMP(value) or TIMESTAMP(value, zone)
29776            _ => {
29777                self.write_keyword("TIMESTAMP");
29778                self.write("(");
29779                if let Some(this) = &e.this {
29780                    self.generate_expression(this)?;
29781                }
29782                if let Some(zone) = &e.zone {
29783                    self.write(", ");
29784                    self.generate_expression(zone)?;
29785                }
29786                self.write(")");
29787            }
29788        }
29789        Ok(())
29790    }
29791
29792    fn generate_timestamp_add(&mut self, e: &TimestampAdd) -> Result<()> {
29793        // TIMESTAMP_ADD(this, expression, unit)
29794        self.write_keyword("TIMESTAMP_ADD");
29795        self.write("(");
29796        self.generate_expression(&e.this)?;
29797        self.write(", ");
29798        self.generate_expression(&e.expression)?;
29799        if let Some(unit) = &e.unit {
29800            self.write(", ");
29801            self.write_keyword(unit);
29802        }
29803        self.write(")");
29804        Ok(())
29805    }
29806
29807    fn generate_timestamp_diff(&mut self, e: &TimestampDiff) -> Result<()> {
29808        // TIMESTAMP_DIFF(this, expression, unit)
29809        self.write_keyword("TIMESTAMP_DIFF");
29810        self.write("(");
29811        self.generate_expression(&e.this)?;
29812        self.write(", ");
29813        self.generate_expression(&e.expression)?;
29814        if let Some(unit) = &e.unit {
29815            self.write(", ");
29816            self.write_keyword(unit);
29817        }
29818        self.write(")");
29819        Ok(())
29820    }
29821
29822    fn generate_timestamp_from_parts(&mut self, e: &TimestampFromParts) -> Result<()> {
29823        // TIMESTAMP_FROM_PARTS(this, expression)
29824        self.write_keyword("TIMESTAMP_FROM_PARTS");
29825        self.write("(");
29826        if let Some(this) = &e.this {
29827            self.generate_expression(this)?;
29828        }
29829        if let Some(expression) = &e.expression {
29830            self.write(", ");
29831            self.generate_expression(expression)?;
29832        }
29833        if let Some(zone) = &e.zone {
29834            self.write(", ");
29835            self.generate_expression(zone)?;
29836        }
29837        if let Some(milli) = &e.milli {
29838            self.write(", ");
29839            self.generate_expression(milli)?;
29840        }
29841        self.write(")");
29842        Ok(())
29843    }
29844
29845    fn generate_timestamp_sub(&mut self, e: &TimestampSub) -> Result<()> {
29846        // TIMESTAMP_SUB(this, INTERVAL expression unit)
29847        self.write_keyword("TIMESTAMP_SUB");
29848        self.write("(");
29849        self.generate_expression(&e.this)?;
29850        self.write(", ");
29851        self.write_keyword("INTERVAL");
29852        self.write_space();
29853        self.generate_expression(&e.expression)?;
29854        if let Some(unit) = &e.unit {
29855            self.write_space();
29856            self.write_keyword(unit);
29857        }
29858        self.write(")");
29859        Ok(())
29860    }
29861
29862    fn generate_timestamp_tz_from_parts(&mut self, e: &TimestampTzFromParts) -> Result<()> {
29863        // TIMESTAMP_TZ_FROM_PARTS(...)
29864        self.write_keyword("TIMESTAMP_TZ_FROM_PARTS");
29865        self.write("(");
29866        if let Some(zone) = &e.zone {
29867            self.generate_expression(zone)?;
29868        }
29869        self.write(")");
29870        Ok(())
29871    }
29872
29873    fn generate_to_binary(&mut self, e: &ToBinary) -> Result<()> {
29874        // TO_BINARY(this, [format])
29875        self.write_keyword("TO_BINARY");
29876        self.write("(");
29877        self.generate_expression(&e.this)?;
29878        if let Some(format) = &e.format {
29879            self.write(", '");
29880            self.write(format);
29881            self.write("'");
29882        }
29883        self.write(")");
29884        Ok(())
29885    }
29886
29887    fn generate_to_boolean(&mut self, e: &ToBoolean) -> Result<()> {
29888        // TO_BOOLEAN(this)
29889        self.write_keyword("TO_BOOLEAN");
29890        self.write("(");
29891        self.generate_expression(&e.this)?;
29892        self.write(")");
29893        Ok(())
29894    }
29895
29896    fn generate_to_char(&mut self, e: &ToChar) -> Result<()> {
29897        // TO_CHAR(this, [format], [nlsparam])
29898        self.write_keyword("TO_CHAR");
29899        self.write("(");
29900        self.generate_expression(&e.this)?;
29901        if let Some(format) = &e.format {
29902            self.write(", '");
29903            self.write(format);
29904            self.write("'");
29905        }
29906        if let Some(nlsparam) = &e.nlsparam {
29907            self.write(", ");
29908            self.generate_expression(nlsparam)?;
29909        }
29910        self.write(")");
29911        Ok(())
29912    }
29913
29914    fn generate_to_decfloat(&mut self, e: &ToDecfloat) -> Result<()> {
29915        // TO_DECFLOAT(this, [format])
29916        self.write_keyword("TO_DECFLOAT");
29917        self.write("(");
29918        self.generate_expression(&e.this)?;
29919        if let Some(format) = &e.format {
29920            self.write(", '");
29921            self.write(format);
29922            self.write("'");
29923        }
29924        self.write(")");
29925        Ok(())
29926    }
29927
29928    fn generate_to_double(&mut self, e: &ToDouble) -> Result<()> {
29929        // TO_DOUBLE(this, [format])
29930        self.write_keyword("TO_DOUBLE");
29931        self.write("(");
29932        self.generate_expression(&e.this)?;
29933        if let Some(format) = &e.format {
29934            self.write(", '");
29935            self.write(format);
29936            self.write("'");
29937        }
29938        self.write(")");
29939        Ok(())
29940    }
29941
29942    fn generate_to_file(&mut self, e: &ToFile) -> Result<()> {
29943        // TO_FILE(this, path)
29944        self.write_keyword("TO_FILE");
29945        self.write("(");
29946        self.generate_expression(&e.this)?;
29947        if let Some(path) = &e.path {
29948            self.write(", ");
29949            self.generate_expression(path)?;
29950        }
29951        self.write(")");
29952        Ok(())
29953    }
29954
29955    fn generate_to_number(&mut self, e: &ToNumber) -> Result<()> {
29956        // TO_NUMBER or TRY_TO_NUMBER (this, [format], [precision], [scale])
29957        // If safe flag is set, output TRY_TO_NUMBER
29958        let is_safe = e.safe.is_some();
29959        if is_safe {
29960            self.write_keyword("TRY_TO_NUMBER");
29961        } else {
29962            self.write_keyword("TO_NUMBER");
29963        }
29964        self.write("(");
29965        self.generate_expression(&e.this)?;
29966        if let Some(format) = &e.format {
29967            self.write(", ");
29968            self.generate_expression(format)?;
29969        }
29970        if let Some(nlsparam) = &e.nlsparam {
29971            self.write(", ");
29972            self.generate_expression(nlsparam)?;
29973        }
29974        if let Some(precision) = &e.precision {
29975            self.write(", ");
29976            self.generate_expression(precision)?;
29977        }
29978        if let Some(scale) = &e.scale {
29979            self.write(", ");
29980            self.generate_expression(scale)?;
29981        }
29982        self.write(")");
29983        Ok(())
29984    }
29985
29986    fn generate_to_table_property(&mut self, e: &ToTableProperty) -> Result<()> {
29987        // TO_TABLE this
29988        self.write_keyword("TO_TABLE");
29989        self.write_space();
29990        self.generate_expression(&e.this)?;
29991        Ok(())
29992    }
29993
29994    fn generate_transaction(&mut self, e: &Transaction) -> Result<()> {
29995        // Check mark to determine the format
29996        let mark_text = e.mark.as_ref().map(|m| {
29997            match m.as_ref() {
29998                Expression::Identifier(id) => id.name.clone(),
29999                Expression::Literal(Literal::String(s)) => s.clone(),
30000                _ => String::new(),
30001            }
30002        });
30003
30004        let is_start = mark_text.as_ref().map_or(false, |s| s == "START");
30005        let has_transaction_keyword = mark_text.as_ref().map_or(false, |s| s == "TRANSACTION");
30006        let has_with_mark = e.mark.as_ref().map_or(false, |m| {
30007            matches!(m.as_ref(), Expression::Literal(Literal::String(_)))
30008        });
30009
30010        if is_start {
30011            // START TRANSACTION [modes]
30012            self.write_keyword("START TRANSACTION");
30013            if let Some(modes) = &e.modes {
30014                self.write_space();
30015                self.generate_expression(modes)?;
30016            }
30017        } else {
30018            // BEGIN [DEFERRED|IMMEDIATE|EXCLUSIVE] [TRANSACTION] [transaction_name] [WITH MARK 'desc']
30019            self.write_keyword("BEGIN");
30020
30021            // Output TRANSACTION keyword if it was present
30022            if has_transaction_keyword || has_with_mark {
30023                self.write_space();
30024                self.write_keyword("TRANSACTION");
30025            }
30026
30027            // Output transaction name or kind (e.g., DEFERRED)
30028            if let Some(this) = &e.this {
30029                self.write_space();
30030                self.generate_expression(this)?;
30031            }
30032
30033            // Output WITH MARK 'description' for TSQL
30034            if has_with_mark {
30035                self.write_space();
30036                self.write_keyword("WITH MARK");
30037                if let Some(Expression::Literal(Literal::String(desc))) = e.mark.as_deref() {
30038                    if !desc.is_empty() {
30039                        self.write_space();
30040                        self.write(&format!("'{}'", desc));
30041                    }
30042                }
30043            }
30044
30045            // Output modes (isolation levels, etc.)
30046            if let Some(modes) = &e.modes {
30047                self.write_space();
30048                self.generate_expression(modes)?;
30049            }
30050        }
30051        Ok(())
30052    }
30053
30054    fn generate_transform(&mut self, e: &Transform) -> Result<()> {
30055        // TRANSFORM(this, expression)
30056        self.write_keyword("TRANSFORM");
30057        self.write("(");
30058        self.generate_expression(&e.this)?;
30059        self.write(", ");
30060        self.generate_expression(&e.expression)?;
30061        self.write(")");
30062        Ok(())
30063    }
30064
30065    fn generate_transform_model_property(&mut self, e: &TransformModelProperty) -> Result<()> {
30066        // TRANSFORM(expressions)
30067        self.write_keyword("TRANSFORM");
30068        self.write("(");
30069        if self.config.pretty && !e.expressions.is_empty() {
30070            self.indent_level += 1;
30071            for (i, expr) in e.expressions.iter().enumerate() {
30072                if i > 0 {
30073                    self.write(",");
30074                }
30075                self.write_newline();
30076                self.write_indent();
30077                self.generate_expression(expr)?;
30078            }
30079            self.indent_level -= 1;
30080            self.write_newline();
30081            self.write(")");
30082        } else {
30083            for (i, expr) in e.expressions.iter().enumerate() {
30084                if i > 0 {
30085                    self.write(", ");
30086                }
30087                self.generate_expression(expr)?;
30088            }
30089            self.write(")");
30090        }
30091        Ok(())
30092    }
30093
30094    fn generate_transient_property(&mut self, e: &TransientProperty) -> Result<()> {
30095        use crate::dialects::DialectType;
30096        // TRANSIENT is Snowflake-specific; skip for other dialects
30097        if let Some(this) = &e.this {
30098            self.generate_expression(this)?;
30099            if matches!(self.config.dialect, Some(DialectType::Snowflake) | None) {
30100                self.write_space();
30101            }
30102        }
30103        if matches!(self.config.dialect, Some(DialectType::Snowflake) | None) {
30104            self.write_keyword("TRANSIENT");
30105        }
30106        Ok(())
30107    }
30108
30109    fn generate_translate(&mut self, e: &Translate) -> Result<()> {
30110        // TRANSLATE(this, from_, to)
30111        self.write_keyword("TRANSLATE");
30112        self.write("(");
30113        self.generate_expression(&e.this)?;
30114        if let Some(from) = &e.from_ {
30115            self.write(", ");
30116            self.generate_expression(from)?;
30117        }
30118        if let Some(to) = &e.to {
30119            self.write(", ");
30120            self.generate_expression(to)?;
30121        }
30122        self.write(")");
30123        Ok(())
30124    }
30125
30126    fn generate_translate_characters(&mut self, e: &TranslateCharacters) -> Result<()> {
30127        // TRANSLATE(this USING expression)
30128        self.write_keyword("TRANSLATE");
30129        self.write("(");
30130        self.generate_expression(&e.this)?;
30131        self.write_space();
30132        self.write_keyword("USING");
30133        self.write_space();
30134        self.generate_expression(&e.expression)?;
30135        if e.with_error.is_some() {
30136            self.write_space();
30137            self.write_keyword("WITH ERROR");
30138        }
30139        self.write(")");
30140        Ok(())
30141    }
30142
30143    fn generate_truncate_table(&mut self, e: &TruncateTable) -> Result<()> {
30144        // TRUNCATE TABLE table1, table2, ...
30145        self.write_keyword("TRUNCATE TABLE");
30146        self.write_space();
30147        for (i, expr) in e.expressions.iter().enumerate() {
30148            if i > 0 {
30149                self.write(", ");
30150            }
30151            self.generate_expression(expr)?;
30152        }
30153        Ok(())
30154    }
30155
30156    fn generate_try_base64_decode_binary(&mut self, e: &TryBase64DecodeBinary) -> Result<()> {
30157        // TRY_BASE64_DECODE_BINARY(this, [alphabet])
30158        self.write_keyword("TRY_BASE64_DECODE_BINARY");
30159        self.write("(");
30160        self.generate_expression(&e.this)?;
30161        if let Some(alphabet) = &e.alphabet {
30162            self.write(", ");
30163            self.generate_expression(alphabet)?;
30164        }
30165        self.write(")");
30166        Ok(())
30167    }
30168
30169    fn generate_try_base64_decode_string(&mut self, e: &TryBase64DecodeString) -> Result<()> {
30170        // TRY_BASE64_DECODE_STRING(this, [alphabet])
30171        self.write_keyword("TRY_BASE64_DECODE_STRING");
30172        self.write("(");
30173        self.generate_expression(&e.this)?;
30174        if let Some(alphabet) = &e.alphabet {
30175            self.write(", ");
30176            self.generate_expression(alphabet)?;
30177        }
30178        self.write(")");
30179        Ok(())
30180    }
30181
30182    fn generate_try_to_decfloat(&mut self, e: &TryToDecfloat) -> Result<()> {
30183        // TRY_TO_DECFLOAT(this, [format])
30184        self.write_keyword("TRY_TO_DECFLOAT");
30185        self.write("(");
30186        self.generate_expression(&e.this)?;
30187        if let Some(format) = &e.format {
30188            self.write(", '");
30189            self.write(format);
30190            self.write("'");
30191        }
30192        self.write(")");
30193        Ok(())
30194    }
30195
30196    fn generate_ts_or_ds_add(&mut self, e: &TsOrDsAdd) -> Result<()> {
30197        // TS_OR_DS_ADD(this, expression, [unit], [return_type])
30198        self.write_keyword("TS_OR_DS_ADD");
30199        self.write("(");
30200        self.generate_expression(&e.this)?;
30201        self.write(", ");
30202        self.generate_expression(&e.expression)?;
30203        if let Some(unit) = &e.unit {
30204            self.write(", ");
30205            self.write_keyword(unit);
30206        }
30207        if let Some(return_type) = &e.return_type {
30208            self.write(", ");
30209            self.generate_expression(return_type)?;
30210        }
30211        self.write(")");
30212        Ok(())
30213    }
30214
30215    fn generate_ts_or_ds_diff(&mut self, e: &TsOrDsDiff) -> Result<()> {
30216        // TS_OR_DS_DIFF(this, expression, [unit])
30217        self.write_keyword("TS_OR_DS_DIFF");
30218        self.write("(");
30219        self.generate_expression(&e.this)?;
30220        self.write(", ");
30221        self.generate_expression(&e.expression)?;
30222        if let Some(unit) = &e.unit {
30223            self.write(", ");
30224            self.write_keyword(unit);
30225        }
30226        self.write(")");
30227        Ok(())
30228    }
30229
30230    fn generate_ts_or_ds_to_date(&mut self, e: &TsOrDsToDate) -> Result<()> {
30231        // TS_OR_DS_TO_DATE(this, [format])
30232        self.write_keyword("TS_OR_DS_TO_DATE");
30233        self.write("(");
30234        self.generate_expression(&e.this)?;
30235        if let Some(format) = &e.format {
30236            self.write(", '");
30237            self.write(format);
30238            self.write("'");
30239        }
30240        self.write(")");
30241        Ok(())
30242    }
30243
30244    fn generate_ts_or_ds_to_time(&mut self, e: &TsOrDsToTime) -> Result<()> {
30245        // TS_OR_DS_TO_TIME(this, [format])
30246        self.write_keyword("TS_OR_DS_TO_TIME");
30247        self.write("(");
30248        self.generate_expression(&e.this)?;
30249        if let Some(format) = &e.format {
30250            self.write(", '");
30251            self.write(format);
30252            self.write("'");
30253        }
30254        self.write(")");
30255        Ok(())
30256    }
30257
30258    fn generate_unhex(&mut self, e: &Unhex) -> Result<()> {
30259        // UNHEX(this, [expression])
30260        self.write_keyword("UNHEX");
30261        self.write("(");
30262        self.generate_expression(&e.this)?;
30263        if let Some(expression) = &e.expression {
30264            self.write(", ");
30265            self.generate_expression(expression)?;
30266        }
30267        self.write(")");
30268        Ok(())
30269    }
30270
30271    fn generate_unicode_string(&mut self, e: &UnicodeString) -> Result<()> {
30272        // U&this [UESCAPE escape]
30273        self.write("U&");
30274        self.generate_expression(&e.this)?;
30275        if let Some(escape) = &e.escape {
30276            self.write_space();
30277            self.write_keyword("UESCAPE");
30278            self.write_space();
30279            self.generate_expression(escape)?;
30280        }
30281        Ok(())
30282    }
30283
30284    fn generate_uniform(&mut self, e: &Uniform) -> Result<()> {
30285        // UNIFORM(this, expression, [gen], [seed])
30286        self.write_keyword("UNIFORM");
30287        self.write("(");
30288        self.generate_expression(&e.this)?;
30289        self.write(", ");
30290        self.generate_expression(&e.expression)?;
30291        if let Some(gen) = &e.gen {
30292            self.write(", ");
30293            self.generate_expression(gen)?;
30294        }
30295        if let Some(seed) = &e.seed {
30296            self.write(", ");
30297            self.generate_expression(seed)?;
30298        }
30299        self.write(")");
30300        Ok(())
30301    }
30302
30303    fn generate_unique_column_constraint(&mut self, e: &UniqueColumnConstraint) -> Result<()> {
30304        // UNIQUE [NULLS NOT DISTINCT] [this] [index_type] [on_conflict] [options]
30305        self.write_keyword("UNIQUE");
30306        // Output NULLS NOT DISTINCT if nulls is set (PostgreSQL 15+ feature)
30307        if e.nulls.is_some() {
30308            self.write(" NULLS NOT DISTINCT");
30309        }
30310        if let Some(this) = &e.this {
30311            self.write_space();
30312            self.generate_expression(this)?;
30313        }
30314        if let Some(index_type) = &e.index_type {
30315            self.write(" USING ");
30316            self.generate_expression(index_type)?;
30317        }
30318        if let Some(on_conflict) = &e.on_conflict {
30319            self.write_space();
30320            self.generate_expression(on_conflict)?;
30321        }
30322        for opt in &e.options {
30323            self.write_space();
30324            self.generate_expression(opt)?;
30325        }
30326        Ok(())
30327    }
30328
30329    fn generate_unique_key_property(&mut self, e: &UniqueKeyProperty) -> Result<()> {
30330        // UNIQUE KEY (expressions)
30331        self.write_keyword("UNIQUE KEY");
30332        self.write(" (");
30333        for (i, expr) in e.expressions.iter().enumerate() {
30334            if i > 0 {
30335                self.write(", ");
30336            }
30337            self.generate_expression(expr)?;
30338        }
30339        self.write(")");
30340        Ok(())
30341    }
30342
30343    fn generate_rollup_property(&mut self, e: &RollupProperty) -> Result<()> {
30344        // ROLLUP (r1(col1, col2), r2(col1))
30345        self.write_keyword("ROLLUP");
30346        self.write(" (");
30347        for (i, index) in e.expressions.iter().enumerate() {
30348            if i > 0 {
30349                self.write(", ");
30350            }
30351            self.generate_identifier(&index.name)?;
30352            self.write("(");
30353            for (j, col) in index.expressions.iter().enumerate() {
30354                if j > 0 {
30355                    self.write(", ");
30356                }
30357                self.generate_identifier(col)?;
30358            }
30359            self.write(")");
30360        }
30361        self.write(")");
30362        Ok(())
30363    }
30364
30365    fn generate_unix_to_str(&mut self, e: &UnixToStr) -> Result<()> {
30366        // UNIX_TO_STR(this, [format])
30367        self.write_keyword("UNIX_TO_STR");
30368        self.write("(");
30369        self.generate_expression(&e.this)?;
30370        if let Some(format) = &e.format {
30371            self.write(", '");
30372            self.write(format);
30373            self.write("'");
30374        }
30375        self.write(")");
30376        Ok(())
30377    }
30378
30379    fn generate_unix_to_time(&mut self, e: &UnixToTime) -> Result<()> {
30380        use crate::dialects::DialectType;
30381        let scale = e.scale.unwrap_or(0); // 0 = seconds
30382
30383        match self.config.dialect {
30384            Some(DialectType::Snowflake) => {
30385                // Snowflake: TO_TIMESTAMP(value[, scale]) - skip scale for seconds (0)
30386                self.write_keyword("TO_TIMESTAMP");
30387                self.write("(");
30388                self.generate_expression(&e.this)?;
30389                if let Some(s) = e.scale {
30390                    if s > 0 {
30391                        self.write(", ");
30392                        self.write(&s.to_string());
30393                    }
30394                }
30395                self.write(")");
30396            }
30397            Some(DialectType::BigQuery) => {
30398                // BigQuery: TIMESTAMP_SECONDS(value) / TIMESTAMP_MILLIS(value)
30399                // or TIMESTAMP_SECONDS(CAST(value / POWER(10, scale) AS INT64)) for other scales
30400                match scale {
30401                    0 => {
30402                        self.write_keyword("TIMESTAMP_SECONDS");
30403                        self.write("(");
30404                        self.generate_expression(&e.this)?;
30405                        self.write(")");
30406                    }
30407                    3 => {
30408                        self.write_keyword("TIMESTAMP_MILLIS");
30409                        self.write("(");
30410                        self.generate_expression(&e.this)?;
30411                        self.write(")");
30412                    }
30413                    6 => {
30414                        self.write_keyword("TIMESTAMP_MICROS");
30415                        self.write("(");
30416                        self.generate_expression(&e.this)?;
30417                        self.write(")");
30418                    }
30419                    _ => {
30420                        // TIMESTAMP_SECONDS(CAST(value / POWER(10, scale) AS INT64))
30421                        self.write_keyword("TIMESTAMP_SECONDS");
30422                        self.write("(CAST(");
30423                        self.generate_expression(&e.this)?;
30424                        self.write(&format!(" / POWER(10, {}) AS INT64))", scale));
30425                    }
30426                }
30427            }
30428            Some(DialectType::Spark) => {
30429                // Spark: CAST(FROM_UNIXTIME(value) AS TIMESTAMP) for scale=0
30430                // TIMESTAMP_MILLIS(value) for scale=3
30431                // TIMESTAMP_MICROS(value) for scale=6
30432                // TIMESTAMP_SECONDS(value / POWER(10, scale)) for other scales
30433                match scale {
30434                    0 => {
30435                        self.write_keyword("CAST");
30436                        self.write("(");
30437                        self.write_keyword("FROM_UNIXTIME");
30438                        self.write("(");
30439                        self.generate_expression(&e.this)?;
30440                        self.write(") ");
30441                        self.write_keyword("AS TIMESTAMP");
30442                        self.write(")");
30443                    }
30444                    3 => {
30445                        self.write_keyword("TIMESTAMP_MILLIS");
30446                        self.write("(");
30447                        self.generate_expression(&e.this)?;
30448                        self.write(")");
30449                    }
30450                    6 => {
30451                        self.write_keyword("TIMESTAMP_MICROS");
30452                        self.write("(");
30453                        self.generate_expression(&e.this)?;
30454                        self.write(")");
30455                    }
30456                    _ => {
30457                        self.write_keyword("TIMESTAMP_SECONDS");
30458                        self.write("(");
30459                        self.generate_expression(&e.this)?;
30460                        self.write(&format!(" / POWER(10, {}))", scale));
30461                    }
30462                }
30463            }
30464            Some(DialectType::Databricks) => {
30465                // Databricks: CAST(FROM_UNIXTIME(value) AS TIMESTAMP) for scale=0
30466                // TIMESTAMP_MILLIS(value) for scale=3
30467                // TIMESTAMP_MICROS(value) for scale=6
30468                match scale {
30469                    0 => {
30470                        self.write_keyword("CAST");
30471                        self.write("(");
30472                        self.write_keyword("FROM_UNIXTIME");
30473                        self.write("(");
30474                        self.generate_expression(&e.this)?;
30475                        self.write(") ");
30476                        self.write_keyword("AS TIMESTAMP");
30477                        self.write(")");
30478                    }
30479                    3 => {
30480                        self.write_keyword("TIMESTAMP_MILLIS");
30481                        self.write("(");
30482                        self.generate_expression(&e.this)?;
30483                        self.write(")");
30484                    }
30485                    6 => {
30486                        self.write_keyword("TIMESTAMP_MICROS");
30487                        self.write("(");
30488                        self.generate_expression(&e.this)?;
30489                        self.write(")");
30490                    }
30491                    _ => {
30492                        self.write_keyword("TIMESTAMP_SECONDS");
30493                        self.write("(");
30494                        self.generate_expression(&e.this)?;
30495                        self.write(&format!(" / POWER(10, {}))", scale));
30496                    }
30497                }
30498            }
30499            Some(DialectType::Presto) | Some(DialectType::Trino) => {
30500                // Presto: FROM_UNIXTIME(CAST(value AS DOUBLE) / POW(10, scale)) for scale > 0
30501                // FROM_UNIXTIME(value) for scale=0
30502                if scale == 0 {
30503                    self.write_keyword("FROM_UNIXTIME");
30504                    self.write("(");
30505                    self.generate_expression(&e.this)?;
30506                    self.write(")");
30507                } else {
30508                    self.write_keyword("FROM_UNIXTIME");
30509                    self.write("(CAST(");
30510                    self.generate_expression(&e.this)?;
30511                    self.write(&format!(" AS DOUBLE) / POW(10, {}))", scale));
30512                }
30513            }
30514            Some(DialectType::DuckDB) => {
30515                // DuckDB: TO_TIMESTAMP(value) for scale=0
30516                // EPOCH_MS(value) for scale=3
30517                // MAKE_TIMESTAMP(value) for scale=6
30518                match scale {
30519                    0 => {
30520                        self.write_keyword("TO_TIMESTAMP");
30521                        self.write("(");
30522                        self.generate_expression(&e.this)?;
30523                        self.write(")");
30524                    }
30525                    3 => {
30526                        self.write_keyword("EPOCH_MS");
30527                        self.write("(");
30528                        self.generate_expression(&e.this)?;
30529                        self.write(")");
30530                    }
30531                    6 => {
30532                        self.write_keyword("MAKE_TIMESTAMP");
30533                        self.write("(");
30534                        self.generate_expression(&e.this)?;
30535                        self.write(")");
30536                    }
30537                    _ => {
30538                        self.write_keyword("TO_TIMESTAMP");
30539                        self.write("(");
30540                        self.generate_expression(&e.this)?;
30541                        self.write(&format!(" / POWER(10, {}))", scale));
30542                        self.write_keyword(" AT TIME ZONE");
30543                        self.write(" 'UTC'");
30544                    }
30545                }
30546            }
30547            Some(DialectType::Redshift) => {
30548                // Redshift: (TIMESTAMP 'epoch' + value * INTERVAL '1 SECOND') for scale=0
30549                // (TIMESTAMP 'epoch' + (value / POWER(10, scale)) * INTERVAL '1 SECOND') for scale > 0
30550                self.write("(TIMESTAMP 'epoch' + ");
30551                if scale == 0 {
30552                    self.generate_expression(&e.this)?;
30553                } else {
30554                    self.write("(");
30555                    self.generate_expression(&e.this)?;
30556                    self.write(&format!(" / POWER(10, {}))", scale));
30557                }
30558                self.write(" * INTERVAL '1 SECOND')");
30559            }
30560            _ => {
30561                // Default: TO_TIMESTAMP(value[, scale])
30562                self.write_keyword("TO_TIMESTAMP");
30563                self.write("(");
30564                self.generate_expression(&e.this)?;
30565                if let Some(s) = e.scale {
30566                    self.write(", ");
30567                    self.write(&s.to_string());
30568                }
30569                self.write(")");
30570            }
30571        }
30572        Ok(())
30573    }
30574
30575    fn generate_unpivot_columns(&mut self, e: &UnpivotColumns) -> Result<()> {
30576        // NAME col VALUE col1, col2, ...
30577        if !matches!(&*e.this, Expression::Null(_)) {
30578            self.write_keyword("NAME");
30579            self.write_space();
30580            self.generate_expression(&e.this)?;
30581        }
30582        if !e.expressions.is_empty() {
30583            self.write_space();
30584            self.write_keyword("VALUE");
30585            self.write_space();
30586            for (i, expr) in e.expressions.iter().enumerate() {
30587                if i > 0 {
30588                    self.write(", ");
30589                }
30590                self.generate_expression(expr)?;
30591            }
30592        }
30593        Ok(())
30594    }
30595
30596    fn generate_user_defined_function(&mut self, e: &UserDefinedFunction) -> Result<()> {
30597        // this(expressions) or (this)(expressions)
30598        if e.wrapped.is_some() {
30599            self.write("(");
30600        }
30601        self.generate_expression(&e.this)?;
30602        if e.wrapped.is_some() {
30603            self.write(")");
30604        }
30605        self.write("(");
30606        for (i, expr) in e.expressions.iter().enumerate() {
30607            if i > 0 {
30608                self.write(", ");
30609            }
30610            self.generate_expression(expr)?;
30611        }
30612        self.write(")");
30613        Ok(())
30614    }
30615
30616    fn generate_using_template_property(&mut self, e: &UsingTemplateProperty) -> Result<()> {
30617        // USING TEMPLATE this
30618        self.write_keyword("USING TEMPLATE");
30619        self.write_space();
30620        self.generate_expression(&e.this)?;
30621        Ok(())
30622    }
30623
30624    fn generate_utc_time(&mut self, _e: &UtcTime) -> Result<()> {
30625        // UTC_TIME
30626        self.write_keyword("UTC_TIME");
30627        Ok(())
30628    }
30629
30630    fn generate_utc_timestamp(&mut self, _e: &UtcTimestamp) -> Result<()> {
30631        // UTC_TIMESTAMP
30632        self.write_keyword("UTC_TIMESTAMP");
30633        Ok(())
30634    }
30635
30636    fn generate_uuid(&mut self, e: &Uuid) -> Result<()> {
30637        use crate::dialects::DialectType;
30638        // Choose UUID function name based on target dialect
30639        let func_name = match self.config.dialect {
30640            Some(DialectType::Snowflake) => "UUID_STRING",
30641            Some(DialectType::PostgreSQL) | Some(DialectType::Redshift) => "GEN_RANDOM_UUID",
30642            Some(DialectType::BigQuery) => "GENERATE_UUID",
30643            _ => {
30644                if let Some(name) = &e.name {
30645                    name.as_str()
30646                } else {
30647                    "UUID"
30648                }
30649            }
30650        };
30651        self.write_keyword(func_name);
30652        self.write("(");
30653        if let Some(this) = &e.this {
30654            self.generate_expression(this)?;
30655        }
30656        self.write(")");
30657        Ok(())
30658    }
30659
30660    fn generate_var_map(&mut self, e: &VarMap) -> Result<()> {
30661        // MAP(key1, value1, key2, value2, ...)
30662        self.write_keyword("MAP");
30663        self.write("(");
30664        let mut first = true;
30665        for (k, v) in e.keys.iter().zip(e.values.iter()) {
30666            if !first {
30667                self.write(", ");
30668            }
30669            self.generate_expression(k)?;
30670            self.write(", ");
30671            self.generate_expression(v)?;
30672            first = false;
30673        }
30674        self.write(")");
30675        Ok(())
30676    }
30677
30678    fn generate_vector_search(&mut self, e: &VectorSearch) -> Result<()> {
30679        // VECTOR_SEARCH(this, column_to_search, query_table, query_column_to_search, top_k, distance_type, ...)
30680        self.write_keyword("VECTOR_SEARCH");
30681        self.write("(");
30682        self.generate_expression(&e.this)?;
30683        if let Some(col) = &e.column_to_search {
30684            self.write(", ");
30685            self.generate_expression(col)?;
30686        }
30687        if let Some(query_table) = &e.query_table {
30688            self.write(", ");
30689            self.generate_expression(query_table)?;
30690        }
30691        if let Some(query_col) = &e.query_column_to_search {
30692            self.write(", ");
30693            self.generate_expression(query_col)?;
30694        }
30695        if let Some(top_k) = &e.top_k {
30696            self.write(", ");
30697            self.generate_expression(top_k)?;
30698        }
30699        if let Some(dist_type) = &e.distance_type {
30700            self.write(", ");
30701            self.generate_expression(dist_type)?;
30702        }
30703        self.write(")");
30704        Ok(())
30705    }
30706
30707    fn generate_version(&mut self, e: &Version) -> Result<()> {
30708        // Python: f"FOR {expression.name} {kind} {expr}"
30709        // e.this = Identifier("TIMESTAMP" or "VERSION")
30710        // e.kind = "AS OF" (or "BETWEEN", etc.)
30711        // e.expression = the value expression
30712        // Hive does NOT use the FOR prefix for time travel
30713        use crate::dialects::DialectType;
30714        let skip_for = matches!(self.config.dialect, Some(DialectType::Hive) | Some(DialectType::Spark));
30715        if !skip_for {
30716            self.write_keyword("FOR");
30717            self.write_space();
30718        }
30719        // Extract the name from this (which is an Identifier expression)
30720        match e.this.as_ref() {
30721            Expression::Identifier(ident) => {
30722                self.write_keyword(&ident.name);
30723            }
30724            _ => {
30725                self.generate_expression(&e.this)?;
30726            }
30727        }
30728        self.write_space();
30729        self.write_keyword(&e.kind);
30730        if let Some(expression) = &e.expression {
30731            self.write_space();
30732            self.generate_expression(expression)?;
30733        }
30734        Ok(())
30735    }
30736
30737    fn generate_view_attribute_property(&mut self, e: &ViewAttributeProperty) -> Result<()> {
30738        // Python: return self.sql(expression, "this")
30739        self.generate_expression(&e.this)?;
30740        Ok(())
30741    }
30742
30743    fn generate_volatile_property(&mut self, e: &VolatileProperty) -> Result<()> {
30744        // Python: return "VOLATILE" if expression.args.get("this") is None else "NOT VOLATILE"
30745        if e.this.is_some() {
30746            self.write_keyword("NOT VOLATILE");
30747        } else {
30748            self.write_keyword("VOLATILE");
30749        }
30750        Ok(())
30751    }
30752
30753    fn generate_watermark_column_constraint(&mut self, e: &WatermarkColumnConstraint) -> Result<()> {
30754        // Python: f"WATERMARK FOR {self.sql(expression, 'this')} AS {self.sql(expression, 'expression')}"
30755        self.write_keyword("WATERMARK FOR");
30756        self.write_space();
30757        self.generate_expression(&e.this)?;
30758        self.write_space();
30759        self.write_keyword("AS");
30760        self.write_space();
30761        self.generate_expression(&e.expression)?;
30762        Ok(())
30763    }
30764
30765    fn generate_week(&mut self, e: &Week) -> Result<()> {
30766        // Python: return self.func("WEEK", expression.this, expression.args.get("mode"))
30767        self.write_keyword("WEEK");
30768        self.write("(");
30769        self.generate_expression(&e.this)?;
30770        if let Some(mode) = &e.mode {
30771            self.write(", ");
30772            self.generate_expression(mode)?;
30773        }
30774        self.write(")");
30775        Ok(())
30776    }
30777
30778    fn generate_when(&mut self, e: &When) -> Result<()> {
30779        // Python: WHEN {matched}{source}{condition} THEN {then}
30780        // matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED"
30781        // source = " BY SOURCE" if MATCHED_BY_SOURCE and expression.args.get("source") else ""
30782        self.write_keyword("WHEN");
30783        self.write_space();
30784
30785        // Check if matched
30786        if let Some(matched) = &e.matched {
30787            // Check the expression - if it's a boolean true, use MATCHED, otherwise NOT MATCHED
30788            match matched.as_ref() {
30789                Expression::Boolean(b) if b.value => {
30790                    self.write_keyword("MATCHED");
30791                }
30792                _ => {
30793                    self.write_keyword("NOT MATCHED");
30794                }
30795            }
30796        } else {
30797            self.write_keyword("NOT MATCHED");
30798        }
30799
30800        // BY SOURCE / BY TARGET
30801        // source = Boolean(true) means BY SOURCE, Boolean(false) means BY TARGET
30802        // BY TARGET is the default and typically omitted in output
30803        // Only emit if the dialect supports BY SOURCE syntax
30804        if self.config.matched_by_source {
30805            if let Some(source) = &e.source {
30806                if let Expression::Boolean(b) = source.as_ref() {
30807                    if b.value {
30808                        // BY SOURCE
30809                        self.write_space();
30810                        self.write_keyword("BY SOURCE");
30811                    }
30812                    // BY TARGET (b.value == false) is omitted as it's the default
30813                } else {
30814                    // For non-boolean source, output as BY SOURCE (legacy behavior)
30815                    self.write_space();
30816                    self.write_keyword("BY SOURCE");
30817                }
30818            }
30819        }
30820
30821        // Condition
30822        if let Some(condition) = &e.condition {
30823            self.write_space();
30824            self.write_keyword("AND");
30825            self.write_space();
30826            self.generate_expression(condition)?;
30827        }
30828
30829        self.write_space();
30830        self.write_keyword("THEN");
30831        self.write_space();
30832
30833        // Generate the then expression (could be INSERT, UPDATE, DELETE)
30834        // MERGE actions are stored as Tuples with the action keyword as first element
30835        self.generate_merge_action(&e.then)?;
30836
30837        Ok(())
30838    }
30839
30840    fn generate_merge_action(&mut self, action: &Expression) -> Result<()> {
30841        match action {
30842            Expression::Tuple(tuple) => {
30843                let elements = &tuple.expressions;
30844                if elements.is_empty() {
30845                    return self.generate_expression(action);
30846                }
30847                // Check if first element is a Var (INSERT, UPDATE, DELETE, etc.)
30848                match &elements[0] {
30849                    Expression::Var(v) if v.this == "INSERT" => {
30850                        self.write_keyword("INSERT");
30851                        let mut values_idx = 1;
30852                        // Check if second element is column list (Tuple)
30853                        if elements.len() > 1 {
30854                            if let Expression::Tuple(cols) = &elements[1] {
30855                                // Could be columns or values - if there's a third element, second is columns
30856                                if elements.len() > 2 {
30857                                    // Second is columns, third is values
30858                                    self.write(" (");
30859                                    for (i, col) in cols.expressions.iter().enumerate() {
30860                                        if i > 0 { self.write(", "); }
30861                                        self.generate_expression(col)?;
30862                                    }
30863                                    self.write(")");
30864                                    values_idx = 2;
30865                                } else {
30866                                    // Only two elements: INSERT + values (no explicit columns)
30867                                    values_idx = 1;
30868                                }
30869                            }
30870                        }
30871                        // Generate VALUES clause
30872                        if values_idx < elements.len() {
30873                            // Check if it's INSERT ROW (BigQuery) — no VALUES keyword needed
30874                            let is_row = matches!(&elements[values_idx], Expression::Var(v) if v.this == "ROW");
30875                            if !is_row {
30876                                self.write_space();
30877                                self.write_keyword("VALUES");
30878                            }
30879                            self.write(" ");
30880                            if let Expression::Tuple(vals) = &elements[values_idx] {
30881                                self.write("(");
30882                                for (i, val) in vals.expressions.iter().enumerate() {
30883                                    if i > 0 { self.write(", "); }
30884                                    self.generate_expression(val)?;
30885                                }
30886                                self.write(")");
30887                            } else {
30888                                self.generate_expression(&elements[values_idx])?;
30889                            }
30890                        }
30891                    }
30892                    Expression::Var(v) if v.this == "UPDATE" => {
30893                        self.write_keyword("UPDATE");
30894                        if elements.len() > 1 {
30895                            self.write_space();
30896                            self.write_keyword("SET");
30897                            // In pretty mode, put assignments on next line with extra indent
30898                            if self.config.pretty {
30899                                self.write_newline();
30900                                self.indent_level += 1;
30901                                self.write_indent();
30902                            } else {
30903                                self.write_space();
30904                            }
30905                            if let Expression::Tuple(assignments) = &elements[1] {
30906                                for (i, assignment) in assignments.expressions.iter().enumerate() {
30907                                    if i > 0 { self.write(", "); }
30908                                    // Strip MERGE target qualifiers from left side of UPDATE SET
30909                                    if !self.merge_strip_qualifiers.is_empty() {
30910                                        self.generate_merge_set_assignment(assignment)?;
30911                                    } else {
30912                                        self.generate_expression(assignment)?;
30913                                    }
30914                                }
30915                            } else {
30916                                self.generate_expression(&elements[1])?;
30917                            }
30918                            if self.config.pretty {
30919                                self.indent_level -= 1;
30920                            }
30921                        }
30922                    }
30923                    _ => {
30924                        // Fallback: generic tuple generation
30925                        self.generate_expression(action)?;
30926                    }
30927                }
30928            }
30929            Expression::Var(v) if v.this == "INSERT" || v.this == "UPDATE" || v.this == "DELETE" || v.this == "DO NOTHING" => {
30930                self.write_keyword(&v.this);
30931            }
30932            _ => {
30933                self.generate_expression(action)?;
30934            }
30935        }
30936        Ok(())
30937    }
30938
30939    /// Generate a MERGE UPDATE SET assignment, stripping target table qualifier from left side
30940    fn generate_merge_set_assignment(&mut self, assignment: &Expression) -> Result<()> {
30941        match assignment {
30942            Expression::Eq(eq) => {
30943                // Strip qualifier from the left side if it matches a MERGE target name
30944                let stripped_left = self.strip_merge_qualifier(&eq.left);
30945                self.generate_expression(&stripped_left)?;
30946                self.write(" = ");
30947                self.generate_expression(&eq.right)?;
30948                Ok(())
30949            }
30950            other => self.generate_expression(other),
30951        }
30952    }
30953
30954    /// Strip table qualifier from a column reference if it matches a MERGE target name
30955    fn strip_merge_qualifier(&self, expr: &Expression) -> Expression {
30956        match expr {
30957            Expression::Column(col) => {
30958                if let Some(ref table_ident) = col.table {
30959                    if self.merge_strip_qualifiers.iter().any(|n| n.eq_ignore_ascii_case(&table_ident.name)) {
30960                        // Strip the table qualifier
30961                        let mut col = col.clone();
30962                        col.table = None;
30963                        return Expression::Column(col);
30964                    }
30965                }
30966                expr.clone()
30967            }
30968            Expression::Dot(dot) => {
30969                // table.column -> column (strip qualifier)
30970                if let Expression::Identifier(id) = &dot.this {
30971                    if self.merge_strip_qualifiers.iter().any(|n| n.eq_ignore_ascii_case(&id.name)) {
30972                        return Expression::Identifier(dot.field.clone());
30973                    }
30974                }
30975                expr.clone()
30976            }
30977            _ => expr.clone(),
30978        }
30979    }
30980
30981    fn generate_whens(&mut self, e: &Whens) -> Result<()> {
30982        // Python: return self.expressions(expression, sep=" ", indent=False)
30983        for (i, expr) in e.expressions.iter().enumerate() {
30984            if i > 0 {
30985                // In pretty mode, each WHEN clause on its own line
30986                if self.config.pretty {
30987                    self.write_newline();
30988                    self.write_indent();
30989                } else {
30990                    self.write_space();
30991                }
30992            }
30993            self.generate_expression(expr)?;
30994        }
30995        Ok(())
30996    }
30997
30998    fn generate_where(&mut self, e: &Where) -> Result<()> {
30999        // Python: return f"{self.seg('WHERE')}{self.sep()}{this}"
31000        self.write_keyword("WHERE");
31001        self.write_space();
31002        self.generate_expression(&e.this)?;
31003        Ok(())
31004    }
31005
31006    fn generate_width_bucket(&mut self, e: &WidthBucket) -> Result<()> {
31007        // Python: return self.func("WIDTH_BUCKET", expression.this, ...)
31008        self.write_keyword("WIDTH_BUCKET");
31009        self.write("(");
31010        self.generate_expression(&e.this)?;
31011        if let Some(min_value) = &e.min_value {
31012            self.write(", ");
31013            self.generate_expression(min_value)?;
31014        }
31015        if let Some(max_value) = &e.max_value {
31016            self.write(", ");
31017            self.generate_expression(max_value)?;
31018        }
31019        if let Some(num_buckets) = &e.num_buckets {
31020            self.write(", ");
31021            self.generate_expression(num_buckets)?;
31022        }
31023        self.write(")");
31024        Ok(())
31025    }
31026
31027    fn generate_window(&mut self, e: &WindowSpec) -> Result<()> {
31028        // Window specification: PARTITION BY ... ORDER BY ... frame
31029        self.generate_window_spec(e)
31030    }
31031
31032    fn generate_window_spec(&mut self, e: &WindowSpec) -> Result<()> {
31033        // Window specification: PARTITION BY ... ORDER BY ... frame
31034        let mut has_content = false;
31035
31036        // PARTITION BY
31037        if !e.partition_by.is_empty() {
31038            self.write_keyword("PARTITION BY");
31039            self.write_space();
31040            for (i, expr) in e.partition_by.iter().enumerate() {
31041                if i > 0 {
31042                    self.write(", ");
31043                }
31044                self.generate_expression(expr)?;
31045            }
31046            has_content = true;
31047        }
31048
31049        // ORDER BY
31050        if !e.order_by.is_empty() {
31051            if has_content {
31052                self.write_space();
31053            }
31054            self.write_keyword("ORDER BY");
31055            self.write_space();
31056            for (i, ordered) in e.order_by.iter().enumerate() {
31057                if i > 0 {
31058                    self.write(", ");
31059                }
31060                self.generate_expression(&ordered.this)?;
31061                if ordered.desc {
31062                    self.write_space();
31063                    self.write_keyword("DESC");
31064                } else if ordered.explicit_asc {
31065                    self.write_space();
31066                    self.write_keyword("ASC");
31067                }
31068                if let Some(nulls_first) = ordered.nulls_first {
31069                    self.write_space();
31070                    self.write_keyword("NULLS");
31071                    self.write_space();
31072                    if nulls_first {
31073                        self.write_keyword("FIRST");
31074                    } else {
31075                        self.write_keyword("LAST");
31076                    }
31077                }
31078            }
31079            has_content = true;
31080        }
31081
31082        // Frame specification
31083        if let Some(frame) = &e.frame {
31084            if has_content {
31085                self.write_space();
31086            }
31087            self.generate_window_frame(frame)?;
31088        }
31089
31090        Ok(())
31091    }
31092
31093    fn generate_with_data_property(&mut self, e: &WithDataProperty) -> Result<()> {
31094        // Python: f"WITH {'NO ' if expression.args.get('no') else ''}DATA"
31095        self.write_keyword("WITH");
31096        self.write_space();
31097        if e.no.is_some() {
31098            self.write_keyword("NO");
31099            self.write_space();
31100        }
31101        self.write_keyword("DATA");
31102
31103        // statistics
31104        if let Some(statistics) = &e.statistics {
31105            self.write_space();
31106            self.write_keyword("AND");
31107            self.write_space();
31108            // Check if statistics is true or false
31109            match statistics.as_ref() {
31110                Expression::Boolean(b) if !b.value => {
31111                    self.write_keyword("NO");
31112                    self.write_space();
31113                }
31114                _ => {}
31115            }
31116            self.write_keyword("STATISTICS");
31117        }
31118        Ok(())
31119    }
31120
31121    fn generate_with_fill(&mut self, e: &WithFill) -> Result<()> {
31122        // Python: f"WITH FILL{from_sql}{to_sql}{step_sql}{interpolate}"
31123        self.write_keyword("WITH FILL");
31124
31125        if let Some(from_) = &e.from_ {
31126            self.write_space();
31127            self.write_keyword("FROM");
31128            self.write_space();
31129            self.generate_expression(from_)?;
31130        }
31131
31132        if let Some(to) = &e.to {
31133            self.write_space();
31134            self.write_keyword("TO");
31135            self.write_space();
31136            self.generate_expression(to)?;
31137        }
31138
31139        if let Some(step) = &e.step {
31140            self.write_space();
31141            self.write_keyword("STEP");
31142            self.write_space();
31143            self.generate_expression(step)?;
31144        }
31145
31146        if let Some(interpolate) = &e.interpolate {
31147            self.write_space();
31148            self.write_keyword("INTERPOLATE");
31149            self.write(" (");
31150            // INTERPOLATE items use reversed alias format: name AS expression
31151            self.generate_interpolate_item(interpolate)?;
31152            self.write(")");
31153        }
31154
31155        Ok(())
31156    }
31157
31158    /// Generate INTERPOLATE items with reversed alias format (name AS expression)
31159    fn generate_interpolate_item(&mut self, expr: &Expression) -> Result<()> {
31160        match expr {
31161            Expression::Alias(alias) => {
31162                // Output as: alias_name AS expression
31163                self.generate_identifier(&alias.alias)?;
31164                self.write_space();
31165                self.write_keyword("AS");
31166                self.write_space();
31167                self.generate_expression(&alias.this)?;
31168            }
31169            Expression::Tuple(tuple) => {
31170                for (i, item) in tuple.expressions.iter().enumerate() {
31171                    if i > 0 { self.write(", "); }
31172                    self.generate_interpolate_item(item)?;
31173                }
31174            }
31175            other => {
31176                self.generate_expression(other)?;
31177            }
31178        }
31179        Ok(())
31180    }
31181
31182    fn generate_with_journal_table_property(&mut self, e: &WithJournalTableProperty) -> Result<()> {
31183        // Python: return f"WITH JOURNAL TABLE={self.sql(expression, 'this')}"
31184        self.write_keyword("WITH JOURNAL TABLE");
31185        self.write("=");
31186        self.generate_expression(&e.this)?;
31187        Ok(())
31188    }
31189
31190    fn generate_with_operator(&mut self, e: &WithOperator) -> Result<()> {
31191        // Python: return f"{self.sql(expression, 'this')} WITH {self.sql(expression, 'op')}"
31192        self.generate_expression(&e.this)?;
31193        self.write_space();
31194        self.write_keyword("WITH");
31195        self.write_space();
31196        self.write_keyword(&e.op);
31197        Ok(())
31198    }
31199
31200    fn generate_with_procedure_options(&mut self, e: &WithProcedureOptions) -> Result<()> {
31201        // Python: return f"WITH {self.expressions(expression, flat=True)}"
31202        self.write_keyword("WITH");
31203        self.write_space();
31204        for (i, expr) in e.expressions.iter().enumerate() {
31205            if i > 0 {
31206                self.write(", ");
31207            }
31208            self.generate_expression(expr)?;
31209        }
31210        Ok(())
31211    }
31212
31213    fn generate_with_schema_binding_property(&mut self, e: &WithSchemaBindingProperty) -> Result<()> {
31214        // Python: return f"WITH {self.sql(expression, 'this')}"
31215        self.write_keyword("WITH");
31216        self.write_space();
31217        self.generate_expression(&e.this)?;
31218        Ok(())
31219    }
31220
31221    fn generate_with_system_versioning_property(&mut self, e: &WithSystemVersioningProperty) -> Result<()> {
31222        // Python: complex logic for SYSTEM_VERSIONING with options
31223        // SYSTEM_VERSIONING=ON(HISTORY_TABLE=..., DATA_CONSISTENCY_CHECK=..., HISTORY_RETENTION_PERIOD=...)
31224        // or SYSTEM_VERSIONING=ON/OFF
31225        // with WITH(...) wrapper if with_ is set
31226
31227        let mut parts = Vec::new();
31228
31229        if let Some(this) = &e.this {
31230            // HISTORY_TABLE=...
31231            let mut s = String::from("HISTORY_TABLE=");
31232            let mut gen = Generator::new();
31233            gen.generate_expression(this)?;
31234            s.push_str(&gen.output);
31235            parts.push(s);
31236        }
31237
31238        if let Some(data_consistency) = &e.data_consistency {
31239            let mut s = String::from("DATA_CONSISTENCY_CHECK=");
31240            let mut gen = Generator::new();
31241            gen.generate_expression(data_consistency)?;
31242            s.push_str(&gen.output);
31243            parts.push(s);
31244        }
31245
31246        if let Some(retention_period) = &e.retention_period {
31247            let mut s = String::from("HISTORY_RETENTION_PERIOD=");
31248            let mut gen = Generator::new();
31249            gen.generate_expression(retention_period)?;
31250            s.push_str(&gen.output);
31251            parts.push(s);
31252        }
31253
31254        self.write_keyword("SYSTEM_VERSIONING");
31255        self.write("=");
31256
31257        if !parts.is_empty() {
31258            self.write_keyword("ON");
31259            self.write("(");
31260            self.write(&parts.join(", "));
31261            self.write(")");
31262        } else if e.on.is_some() {
31263            self.write_keyword("ON");
31264        } else {
31265            self.write_keyword("OFF");
31266        }
31267
31268        // Wrap in WITH(...) if with_ is set
31269        if e.with_.is_some() {
31270            let inner = self.output.clone();
31271            self.output.clear();
31272            self.write("WITH(");
31273            self.write(&inner);
31274            self.write(")");
31275        }
31276
31277        Ok(())
31278    }
31279
31280    fn generate_with_table_hint(&mut self, e: &WithTableHint) -> Result<()> {
31281        // Python: f"WITH ({self.expressions(expression, flat=True)})"
31282        self.write_keyword("WITH");
31283        self.write(" (");
31284        for (i, expr) in e.expressions.iter().enumerate() {
31285            if i > 0 {
31286                self.write(", ");
31287            }
31288            self.generate_expression(expr)?;
31289        }
31290        self.write(")");
31291        Ok(())
31292    }
31293
31294    fn generate_xml_element(&mut self, e: &XMLElement) -> Result<()> {
31295        // Python: prefix = "EVALNAME" if expression.args.get("evalname") else "NAME"
31296        // return self.func("XMLELEMENT", name, *expression.expressions)
31297        self.write_keyword("XMLELEMENT");
31298        self.write("(");
31299
31300        if e.evalname.is_some() {
31301            self.write_keyword("EVALNAME");
31302        } else {
31303            self.write_keyword("NAME");
31304        }
31305        self.write_space();
31306        self.generate_expression(&e.this)?;
31307
31308        for expr in &e.expressions {
31309            self.write(", ");
31310            self.generate_expression(expr)?;
31311        }
31312        self.write(")");
31313        Ok(())
31314    }
31315
31316    fn generate_xml_get(&mut self, e: &XMLGet) -> Result<()> {
31317        // XMLGET(this, expression [, instance])
31318        self.write_keyword("XMLGET");
31319        self.write("(");
31320        self.generate_expression(&e.this)?;
31321        self.write(", ");
31322        self.generate_expression(&e.expression)?;
31323        if let Some(instance) = &e.instance {
31324            self.write(", ");
31325            self.generate_expression(instance)?;
31326        }
31327        self.write(")");
31328        Ok(())
31329    }
31330
31331    fn generate_xml_key_value_option(&mut self, e: &XMLKeyValueOption) -> Result<()> {
31332        // Python: this + optional (expr)
31333        self.generate_expression(&e.this)?;
31334        if let Some(expression) = &e.expression {
31335            self.write("(");
31336            self.generate_expression(expression)?;
31337            self.write(")");
31338        }
31339        Ok(())
31340    }
31341
31342    fn generate_xml_table(&mut self, e: &XMLTable) -> Result<()> {
31343        // Python: XMLTABLE(namespaces + this + passing + by_ref + columns)
31344        self.write_keyword("XMLTABLE");
31345        self.write("(");
31346
31347        if self.config.pretty {
31348            self.indent_level += 1;
31349            self.write_newline();
31350            self.write_indent();
31351            self.generate_expression(&e.this)?;
31352
31353            if let Some(passing) = &e.passing {
31354                self.write_newline();
31355                self.write_indent();
31356                self.write_keyword("PASSING");
31357                if let Expression::Tuple(tuple) = passing.as_ref() {
31358                    for expr in &tuple.expressions {
31359                        self.write_newline();
31360                        self.indent_level += 1;
31361                        self.write_indent();
31362                        self.generate_expression(expr)?;
31363                        self.indent_level -= 1;
31364                    }
31365                } else {
31366                    self.write_newline();
31367                    self.indent_level += 1;
31368                    self.write_indent();
31369                    self.generate_expression(passing)?;
31370                    self.indent_level -= 1;
31371                }
31372            }
31373
31374            if e.by_ref.is_some() {
31375                self.write_newline();
31376                self.write_indent();
31377                self.write_keyword("RETURNING SEQUENCE BY REF");
31378            }
31379
31380            if !e.columns.is_empty() {
31381                self.write_newline();
31382                self.write_indent();
31383                self.write_keyword("COLUMNS");
31384                for (i, col) in e.columns.iter().enumerate() {
31385                    self.write_newline();
31386                    self.indent_level += 1;
31387                    self.write_indent();
31388                    self.generate_expression(col)?;
31389                    self.indent_level -= 1;
31390                    if i < e.columns.len() - 1 {
31391                        self.write(",");
31392                    }
31393                }
31394            }
31395
31396            self.indent_level -= 1;
31397            self.write_newline();
31398            self.write_indent();
31399            self.write(")");
31400            return Ok(());
31401        }
31402
31403        // Namespaces - unwrap Tuple to generate comma-separated list without parentheses
31404        if let Some(namespaces) = &e.namespaces {
31405            self.write_keyword("XMLNAMESPACES");
31406            self.write("(");
31407            // Unwrap Tuple if present to avoid extra parentheses
31408            if let Expression::Tuple(tuple) = namespaces.as_ref() {
31409                for (i, expr) in tuple.expressions.iter().enumerate() {
31410                    if i > 0 {
31411                        self.write(", ");
31412                    }
31413                    // Python pattern: if it's an Alias, output as-is; otherwise prepend DEFAULT
31414                    // See xmlnamespace_sql in generator.py
31415                    if !matches!(expr, Expression::Alias(_)) {
31416                        self.write_keyword("DEFAULT");
31417                        self.write_space();
31418                    }
31419                    self.generate_expression(expr)?;
31420                }
31421            } else {
31422                // Single namespace - check if DEFAULT
31423                if !matches!(namespaces.as_ref(), Expression::Alias(_)) {
31424                    self.write_keyword("DEFAULT");
31425                    self.write_space();
31426                }
31427                self.generate_expression(namespaces)?;
31428            }
31429            self.write("), ");
31430        }
31431
31432        // XPath expression
31433        self.generate_expression(&e.this)?;
31434
31435        // PASSING clause - unwrap Tuple to generate comma-separated list without parentheses
31436        if let Some(passing) = &e.passing {
31437            self.write_space();
31438            self.write_keyword("PASSING");
31439            self.write_space();
31440            // Unwrap Tuple if present to avoid extra parentheses
31441            if let Expression::Tuple(tuple) = passing.as_ref() {
31442                for (i, expr) in tuple.expressions.iter().enumerate() {
31443                    if i > 0 {
31444                        self.write(", ");
31445                    }
31446                    self.generate_expression(expr)?;
31447                }
31448            } else {
31449                self.generate_expression(passing)?;
31450            }
31451        }
31452
31453        // RETURNING SEQUENCE BY REF
31454        if e.by_ref.is_some() {
31455            self.write_space();
31456            self.write_keyword("RETURNING SEQUENCE BY REF");
31457        }
31458
31459        // COLUMNS clause
31460        if !e.columns.is_empty() {
31461            self.write_space();
31462            self.write_keyword("COLUMNS");
31463            self.write_space();
31464            for (i, col) in e.columns.iter().enumerate() {
31465                if i > 0 {
31466                    self.write(", ");
31467                }
31468                self.generate_expression(col)?;
31469            }
31470        }
31471
31472        self.write(")");
31473        Ok(())
31474    }
31475
31476    fn generate_xor(&mut self, e: &Xor) -> Result<()> {
31477        // Python: return self.connector_sql(expression, "XOR", stack)
31478        // Handles: this XOR expression or expressions joined by XOR
31479        if let Some(this) = &e.this {
31480            self.generate_expression(this)?;
31481            if let Some(expression) = &e.expression {
31482                self.write_space();
31483                self.write_keyword("XOR");
31484                self.write_space();
31485                self.generate_expression(expression)?;
31486            }
31487        }
31488
31489        // Handle multiple expressions
31490        for (i, expr) in e.expressions.iter().enumerate() {
31491            if i > 0 || e.this.is_some() {
31492                self.write_space();
31493                self.write_keyword("XOR");
31494                self.write_space();
31495            }
31496            self.generate_expression(expr)?;
31497        }
31498        Ok(())
31499    }
31500
31501    fn generate_zipf(&mut self, e: &Zipf) -> Result<()> {
31502        // ZIPF(this, elementcount [, gen])
31503        self.write_keyword("ZIPF");
31504        self.write("(");
31505        self.generate_expression(&e.this)?;
31506        if let Some(elementcount) = &e.elementcount {
31507            self.write(", ");
31508            self.generate_expression(elementcount)?;
31509        }
31510        if let Some(gen) = &e.gen {
31511            self.write(", ");
31512            self.generate_expression(gen)?;
31513        }
31514        self.write(")");
31515        Ok(())
31516    }
31517}
31518
31519impl Default for Generator {
31520    fn default() -> Self {
31521        Self::new()
31522    }
31523}
31524
31525#[cfg(test)]
31526mod tests {
31527    use super::*;
31528    use crate::parser::Parser;
31529
31530    fn roundtrip(sql: &str) -> String {
31531        let ast = Parser::parse_sql(sql).unwrap();
31532        Generator::sql(&ast[0]).unwrap()
31533    }
31534
31535    #[test]
31536    fn test_simple_select() {
31537        let result = roundtrip("SELECT 1");
31538        assert_eq!(result, "SELECT 1");
31539    }
31540
31541    #[test]
31542    fn test_select_from() {
31543        let result = roundtrip("SELECT a, b FROM t");
31544        assert_eq!(result, "SELECT a, b FROM t");
31545    }
31546
31547    #[test]
31548    fn test_select_where() {
31549        let result = roundtrip("SELECT * FROM t WHERE x = 1");
31550        assert_eq!(result, "SELECT * FROM t WHERE x = 1");
31551    }
31552
31553    #[test]
31554    fn test_select_join() {
31555        let result = roundtrip("SELECT * FROM a JOIN b ON a.id = b.id");
31556        assert_eq!(result, "SELECT * FROM a JOIN b ON a.id = b.id");
31557    }
31558
31559    #[test]
31560    fn test_insert() {
31561        let result = roundtrip("INSERT INTO t (a, b) VALUES (1, 2)");
31562        assert_eq!(result, "INSERT INTO t (a, b) VALUES (1, 2)");
31563    }
31564
31565    #[test]
31566    fn test_pretty_print() {
31567        let ast = Parser::parse_sql("SELECT a, b FROM t WHERE x = 1").unwrap();
31568        let result = Generator::pretty_sql(&ast[0]).unwrap();
31569        assert!(result.contains('\n'));
31570    }
31571
31572    #[test]
31573    fn test_window_function() {
31574        let result = roundtrip("SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)");
31575        assert_eq!(result, "SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id)");
31576    }
31577
31578    #[test]
31579    fn test_window_function_with_frame() {
31580        let result = roundtrip("SELECT SUM(amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)");
31581        assert_eq!(result, "SELECT SUM(amount) OVER (ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)");
31582    }
31583
31584    #[test]
31585    fn test_aggregate_with_filter() {
31586        let result = roundtrip("SELECT COUNT(*) FILTER (WHERE status = 1) FROM orders");
31587        assert_eq!(result, "SELECT COUNT(*) FILTER(WHERE status = 1) FROM orders");
31588    }
31589
31590    #[test]
31591    fn test_subscript() {
31592        let result = roundtrip("SELECT arr[0]");
31593        assert_eq!(result, "SELECT arr[0]");
31594    }
31595
31596    // DDL tests
31597    #[test]
31598    fn test_create_table() {
31599        let result = roundtrip("CREATE TABLE users (id INT, name VARCHAR(100))");
31600        assert_eq!(result, "CREATE TABLE users (id INT, name VARCHAR(100))");
31601    }
31602
31603    #[test]
31604    fn test_create_table_with_constraints() {
31605        let result = roundtrip("CREATE TABLE users (id INT PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL)");
31606        assert_eq!(result, "CREATE TABLE users (id INT PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL)");
31607    }
31608
31609    #[test]
31610    fn test_create_table_if_not_exists() {
31611        let result = roundtrip("CREATE TABLE IF NOT EXISTS t (id INT)");
31612        assert_eq!(result, "CREATE TABLE IF NOT EXISTS t (id INT)");
31613    }
31614
31615    #[test]
31616    fn test_drop_table() {
31617        let result = roundtrip("DROP TABLE users");
31618        assert_eq!(result, "DROP TABLE users");
31619    }
31620
31621    #[test]
31622    fn test_drop_table_if_exists_cascade() {
31623        let result = roundtrip("DROP TABLE IF EXISTS users CASCADE");
31624        assert_eq!(result, "DROP TABLE IF EXISTS users CASCADE");
31625    }
31626
31627    #[test]
31628    fn test_alter_table_add_column() {
31629        let result = roundtrip("ALTER TABLE users ADD COLUMN email VARCHAR(255)");
31630        assert_eq!(result, "ALTER TABLE users ADD COLUMN email VARCHAR(255)");
31631    }
31632
31633    #[test]
31634    fn test_alter_table_drop_column() {
31635        let result = roundtrip("ALTER TABLE users DROP COLUMN email");
31636        assert_eq!(result, "ALTER TABLE users DROP COLUMN email");
31637    }
31638
31639    #[test]
31640    fn test_create_index() {
31641        let result = roundtrip("CREATE INDEX idx_name ON users(name)");
31642        assert_eq!(result, "CREATE INDEX idx_name ON users(name)");
31643    }
31644
31645    #[test]
31646    fn test_create_unique_index() {
31647        let result = roundtrip("CREATE UNIQUE INDEX idx_email ON users(email)");
31648        assert_eq!(result, "CREATE UNIQUE INDEX idx_email ON users(email)");
31649    }
31650
31651    #[test]
31652    fn test_drop_index() {
31653        let result = roundtrip("DROP INDEX idx_name");
31654        assert_eq!(result, "DROP INDEX idx_name");
31655    }
31656
31657    #[test]
31658    fn test_create_view() {
31659        let result = roundtrip("CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1");
31660        assert_eq!(result, "CREATE VIEW active_users AS SELECT * FROM users WHERE active = 1");
31661    }
31662
31663    #[test]
31664    fn test_drop_view() {
31665        let result = roundtrip("DROP VIEW active_users");
31666        assert_eq!(result, "DROP VIEW active_users");
31667    }
31668
31669    #[test]
31670    fn test_truncate() {
31671        let result = roundtrip("TRUNCATE TABLE users");
31672        assert_eq!(result, "TRUNCATE TABLE users");
31673    }
31674
31675    #[test]
31676    fn test_string_literal_escaping_default() {
31677        // Default: double single quotes
31678        let result = roundtrip("SELECT 'hello'");
31679        assert_eq!(result, "SELECT 'hello'");
31680
31681        // Single quotes are doubled
31682        let result = roundtrip("SELECT 'it''s a test'");
31683        assert_eq!(result, "SELECT 'it''s a test'");
31684    }
31685
31686    #[test]
31687    fn test_string_literal_escaping_mysql() {
31688        use crate::dialects::DialectType;
31689
31690        let config = GeneratorConfig {
31691            dialect: Some(DialectType::MySQL),
31692            ..Default::default()
31693        };
31694
31695        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31696        let mut gen = Generator::with_config(config.clone());
31697        let result = gen.generate(&ast[0]).unwrap();
31698        assert_eq!(result, "SELECT 'hello'");
31699
31700        // MySQL uses SQL standard quote doubling for escaping (matches Python sqlglot)
31701        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31702        let mut gen = Generator::with_config(config.clone());
31703        let result = gen.generate(&ast[0]).unwrap();
31704        assert_eq!(result, "SELECT 'it''s'");
31705    }
31706
31707    #[test]
31708    fn test_string_literal_escaping_postgres() {
31709        use crate::dialects::DialectType;
31710
31711        let config = GeneratorConfig {
31712            dialect: Some(DialectType::PostgreSQL),
31713            ..Default::default()
31714        };
31715
31716        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31717        let mut gen = Generator::with_config(config.clone());
31718        let result = gen.generate(&ast[0]).unwrap();
31719        assert_eq!(result, "SELECT 'hello'");
31720
31721        // PostgreSQL uses doubled quotes for regular strings
31722        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31723        let mut gen = Generator::with_config(config.clone());
31724        let result = gen.generate(&ast[0]).unwrap();
31725        assert_eq!(result, "SELECT 'it''s'");
31726    }
31727
31728    #[test]
31729    fn test_string_literal_escaping_bigquery() {
31730        use crate::dialects::DialectType;
31731
31732        let config = GeneratorConfig {
31733            dialect: Some(DialectType::BigQuery),
31734            ..Default::default()
31735        };
31736
31737        let ast = Parser::parse_sql("SELECT 'hello'").unwrap();
31738        let mut gen = Generator::with_config(config.clone());
31739        let result = gen.generate(&ast[0]).unwrap();
31740        assert_eq!(result, "SELECT 'hello'");
31741
31742        // BigQuery escapes single quotes with backslash
31743        let ast = Parser::parse_sql("SELECT 'it''s'").unwrap();
31744        let mut gen = Generator::with_config(config.clone());
31745        let result = gen.generate(&ast[0]).unwrap();
31746        assert_eq!(result, "SELECT 'it\\'s'");
31747    }
31748
31749}